ChloeGet Started

Reference

@chloejs/core/services

The work a job does without asking anybody, called from a step.

What a job can do without asking anybody: running a command, sending mail, reading mail, reading and writing files in one folder, running one of an agent's own scripts, reading a web page.

import { run, deliverEmail } from "@chloejs/core/services";

The same work offered to a model instead is "@chloejs/core/tools/", and each of those is a wrapper over one of these. Same rule as index.ts: adding a name here is publishing it.

deliverEmaileditFilesEmailSenderfeedToTextfolderTreehtmlToTextisPrivatelistFileslistScriptsmarkdownToHtmlmarkdownToTextPagereadFilesreadPageResultrunrunScriptssearchFileswriteFiles

deliverEmailfunction

export async function deliverEmail(
  { from, to, tag, replyTo, markdown }: EmailSender,
  subject: string,
  body: string,
  provider?: SendingProvider,
): Promise<{ sent: true; id?: string; subject: string }>

Sends one email and returns its id. The tag is put in front of the subject. It goes by provider, else by email.provider in settings, and by nothing when settings say "none".

services/emailService.ts:76

editFilesfunction

export async function editFiles(
  root: string,
  path: string,
  old: string,
  replacement: string,
  options: { commit?: boolean; message?: string; author?: string; in?: Place } = {},
)

Change one part of a file: old must appear in it exactly once, and is replaced by new, so a small change to a big file never sends the whole file. Throws when old is not there or is there more than once, saying which, so the caller can add the text around it. An empty new deletes old. commit, message, author and in are as for writeFiles.

services/filesService.ts:252

EmailSenderinterface

export interface EmailSender {
  /** The From line, e.g. "Backups <info@example.com>". */
  from: string;
  /** Who it goes to. */
  to: string[];
  /** Prefix put in front of every subject, so an inbox can be filtered. */
  tag?: string;
  /** Where a reply goes, when not to `from`: a sending address that cannot receive mail needs this. */
  replyTo?: string[];
  /**
   * The body is Markdown: it is sent as HTML with headings, lists, tables and
   * links, plus a plain text copy without the symbols. Off, it is sent as
   * written, as plain text only.
   */
  markdown?: boolean;
}

Who an agent's mail comes from, who it goes to, and the tag in front of every subject.

services/emailService.ts:20

feedToTextfunction

export function feedToText(xml: string, base: string): { title?: string; text: string }

An Atom or RSS feed as text: each entry is its title as a link, who wrote it and when, then its words. Raw, a feed's words are HTML escaped inside XML, and a slice of it holds a few entries where this holds a page of them.

services/webService.ts:197

folderTreefunction

export async function folderTree(root: string, { depth = 2, most = 200 } = {}): Promise<string[]>

The folders inside root, depth levels down, one a line, each level indented two spaces further. Files, hidden folders and the ones no file tool may open are left out. At most most lines, then a line saying more were left out.

services/filesService.ts:40

htmlToTextfunction

export function htmlToText(html: string, base: string): { title?: string; text: string }

HTML to readable text: blocks become lines, cells are split by " | ", links keep their address.

services/webService.ts:156

isPrivatefunction

export function isPrivate(address: string): boolean

Loopback, private ranges, link local, multicast, and the same in IPv6, including an IPv4 address carried inside an IPv6 one in any spelling.

services/webService.ts:55

listFilesfunction

export async function listFiles(root: string, path?: string)

List a folder. path is relative to root, and omitting it means the top.

services/filesService.ts:22

listScriptsfunction

export async function listScripts(agent: string): Promise<string[]>

What this agent has in scripts/, sorted. Nothing hidden.

services/scriptsService.ts:17

markdownToHtmlfunction

export function markdownToHtml(markdown: string): string

Markdown as email HTML: ## headings, - lists, | tables, > quotes, fenced code, bold, code and links. Lines next to each other are one paragraph, so a body wrapped at any width reads as prose rather than as a ragged line per paragraph. Only a blank line starts a new one.

services/emailService.ts:123

markdownToTextfunction

export function markdownToText(markdown: string): string

The same Markdown as plain text: no #, ** or backticks, a table row as a: b, a link as words (address).

services/emailService.ts:193

Pageinterface

export interface Page {
  url: string;
  status: number;
  title?: string;
  /** The page as plain text, links written as `[text](url)`. */
  text: string;
  /** Where the next slice starts, when the page was longer than one. */
  next?: number;
}

One fetched web page as plain text, in slices when it is longer than one.

services/webService.ts:15

readFilesfunction

export async function readFiles(
  root: string,
  path: string,
  { from, lines, limit }: { from?: number; lines?: number; limit?: number } = {},
)

Read one file. path is relative to root and cannot leave it.

With from (the first line, counting from 1) or lines (how many), only that part comes back, with from, to and the file's totalLines. With limit (characters) and no range, a longer file comes back cut at the last whole line that fits, saying so, so a big file is not read whole by accident. With none of the three it is the whole file, as it always was.

services/filesService.ts:69

readPagefunction

export async function readPage(address: string, from = 0): Promise<Page>

Fetches address and returns it as text, a slice at a time starting at from.

services/webService.ts:220

Resultinterface

export interface Result {
  exitCode: number;
  stdout: string;
  stderr: string;
}

What a command came back with.

services/runService.ts:8

runfunction

export function run(
  file: string,
  args: string[],
  options: { timeoutMs?: number; cwd?: string; env?: Record<string, string> } = {},
): Promise<Result>

Runs one command with its arguments, never through a shell, and cuts output that is very long.

services/runService.ts:26

runScriptsfunction

export async function runScripts(
  agent: string,
  name: string,
  args: string[] = [],
  { timeoutMs = 300_000, cwd }: { timeoutMs?: number; cwd?: string } = {},
): Promise<Result & { script: string; args: string[] }>

Run one. A name not on disk is refused rather than resolved as a path, which is what stops a ../ in a name reaching anything else on the box.

cwd defaults to the scripts folder, so a script may use relative paths. An agent whose scripts work on a tree somewhere else passes that instead.

services/scriptsService.ts:33

searchFilesfunction

export async function searchFiles(root: string, query: string, folder?: string, { around = 0 }: { around?: number } = {})

Search a folder for text, case-insensitive and as written, not as a pattern. folder narrows it, to a folder or one file. Paths in the results are relative to root, the way the other functions here take them. With around, each match comes with that many lines either side, and matches close together in one file share one block, so a match can often be understood without reading the file. Five matches a file at most, and results stops at 80 matches, or 30 with around; matches counts what was found before that.

services/filesService.ts:120

writeFilesfunction

export async function writeFiles(
  root: string,
  path: string,
  content: string,
  {
    commit = false,
    message,
    append = false,
    author,
    in: place,
  }: { commit?: boolean; message?: string; append?: boolean; author?: string; in?: Place } = {},
)

Write one file, replacing it, or with append adding to the end of it on a line of its own, so a long file that only grows is never written out whole. commit makes the write a git commit of that file alone, for a folder in a repo, and then message is required. author is the agent it is written under, and in which of its places this is, so the run writing it lists the commit. Without an author it is this box's own git name.

services/filesService.ts:211