@oddington/cli
v0.1.87
Published
OS command-line client
Readme
OS CLI
The CLI lets people and routines work with their OS from a terminal.
Install
The CLI requires Node.js 22.12 or later.
npm install --global @oddington/cliUse npm as the single global installer for this command; parallel Bun, pnpm or
other global copies can leave an older os earlier on PATH. Confirm the
installed release with os --version.
Sign in
os login
os whoami
os workspace switch example
os workspace switch
os logoutIf you have several workspaces, use ↑/↓ to highlight one and Enter to select it. Ctrl+C cancels without storing a new login. A single workspace is selected automatically; plain terminals retain the numbered-input fallback.
Use os workspace switch <slug> with an identifier from os whoami, or omit
it to choose in the terminal. Switching reuses your login without browser
authorization. It affects subsequent commands across terminals and chats on
this computer; started Runs and apps keep their captured workspace.
Login and switching save the selected workspace's repository locations with your
secure session. os data push uses those saved settings and ordinary
Git, without calling the OS API. Existing os connection target list, add and
disable commands, and os repository add, also save the selected workspace's
current locations. Until those settings are reloaded, the saved destination
remains in use; each push shows where it is going, and Git enforces current access
and branch rules.
Missing repository settings do not invalidate your login. Push stops with a hint
for the existing Git target and data repository commands; it never loads settings
automatically or requires a new sign-in just for repository setup.
Run os --help or add --help to any command for the full command list.
Commands print readable output by default. Add --json when another program
will consume the result.
os app dev <name> shows recent development-server output in your terminal if
startup fails or the server later stops. It distinguishes an exit from a startup
timeout and includes the app's retry command. This is output from your local dev
script; OS keeps only a bounded tail in memory and does not save or upload it.
Successful startup stays quiet apart from OS progress, and Vite handles ordinary
compilation errors through its browser overlay while you edit.
Dropbox connection setup uses an offline OAuth URL and hidden one-time code entry:
os connection create dropbox --workspace example --name business --root "/Business"
os connection list --workspace example --all
os connection inspect --workspace example --id <returned-id>
os connection test --workspace example --connection dropbox/businessA Dropbox connection lets routines work with an existing Dropbox folder. Its root is the base path trusted routines must use, not provider-enforced token containment. Setup and testing only read provider state; routine calls change Dropbox immediately, independently of publication. Disconnect prevents new delivery but does not revoke an already delivered token.
Dropbox storage backs the OS Files capability with a separate dedicated folder:
os files storage connect dropbox --workspace example --name os-files
os files storage select --workspace example --connection dropbox/os-filesConnecting authorizes storage; selecting it separately changes placement for new remote Files. A routine connection cannot be selected as File storage, and routines never receive storage credentials. OS uses separate company-owned OAuth applications for these purposes. Members authorize their own accounts; they do not register applications or handle OS app keys. Missing service setup names either Dropbox connection authorization or Dropbox storage authorization.
The old --access full-dropbox --root ... connection flag remains a compatibility
alias. A connection command without --root, or with --access app-folder, now
points to the explicit Files storage command; it never silently changes purpose.
Older installed clients retain their explicit authorization semantics on the API.
Deployment operators must complete the credential-name cutover in
server/dropbox-authorization-rollout.md before deploying this server version.
Sign-in and session operations use https://auth.oddington.ai. Connection
management, repository targets, local app repository context, Dropbox authorization,
remote File-storage configuration and Routine deployment use https://api.oddington.ai. These addresses are fixed; workspace
configuration and OS_ORIGIN do not override them.
Connection use is the canonical POST /workspaces/<workspace>/connections/use
operation on the API host. The verified member or retained Run supplies authority;
the request names the Connection and exact Run ID. Hosted ad hoc execution needs
both OS_CONNECTION_RUN_TOKEN and OS_CONNECTION_RUN_ID.
Inbox publication goes directly to the selected workspace's /api/inbox/check,
/api/inbox/publish and /api/inbox/resolve operations. File intake and reads
use shared Files through /api/files.
Local routine Data reads and writes current schema-governed checkout files.
Files and Inbox operations complete during the call. A later failure retains
completed local paths and hosted effects in local Run history.
Add a Data repository
Use an existing, enabled Git repository in your workspace:
os repository add project-management --workspace example --git-repository workspaceThe Data repository name selects the top-level project-management/ directory.
OS checks its collection schemas, saves the binding and starts preparing the data.
The result reports preparing or ready; repeating the same command resumes setup.
Configure the Git repository separately through Connections.
Push local data
os data push project-management
os routine run supplier-invoices --input january.json --pushos data push commits current file changes inside the selected data directory and
pushes them to its configured Git destination. It includes manual edits, schemas,
new files and deletions. Uncommitted files outside that directory stay untouched,
including staged code. With one matching data repository, you can omit its name.
Existing local commits are part of a normal Git push. If any change files outside the selected data directory, OS lists them and asks for confirmation before committing. Declining leaves your files and staging unchanged. With no data changes to send, it does nothing. Use your ordinary Git identity and credentials; your OS login saves the configured destination during workspace selection.
--push runs only after a successful local routine with one declared writable
data repository. It includes earlier local edits in that directory. If pushing
fails, the routine's result and local data commit are kept: retry os data push,
without rerunning the routine. Remote conflicts require normal Git recovery.
--json returns one structured outcome and never asks for confirmation.
Repository schema types
Inside a TypeScript code repository with a GitHub origin and named branch, run:
os data schema generalYour ordinary CLI login resolves the worktree to one member workspace. general
is a Data repository alias there. The command reads only its published normalized
schema and writes the marked root os-data.generated.d.ts. It reports that path
and the TypeScript project files it integrates. Review and commit those files;
a fresh clone uses the types without signing in or regenerating them.
The shared declaration contains no package import. The worktree root needs no
package installation; each consuming project keeps its own compatible
@oddington/sdk dependency.
For unpublished schema changes in this checkout, run:
os data schema general --localThis reads only _schema.yaml files beneath general/, including uncommitted
and untracked schemas. It needs no login, network, remote or named branch.
It updates the same generated file and TypeScript settings and preserves other
repository aliases. The command reports whether it used local or published
schemas. Invalid local schemas stop generation without replacing your existing
types; there is no fallback to published schemas. Run it again after changing a
local schema. App development does not automatically regenerate types.
Keep importing data from @oddington/sdk. A known repository literal now gains
schema-aware records.query, pages and all queries and projected values.
No source import, app declaration, build or launch is needed. Other operations
and dynamic repository names retain their generic types. Types grant no access;
runtime Data validation and membership remain authoritative.
Run the same command explicitly after a published schema changes, then review and commit its diff. Other aliases in the declaration are preserved. An unchanged refresh leaves generated and project bytes untouched. App build/launch never refreshes this file. The command does not retrieve Records or commit anything. Exact unedited output from the first released module-augmentation format is upgraded atomically on refresh, preserving other aliases. Edited, malformed or unrecognized generated files remain protected from replacement.
Project integration preserves JSONC comments and authored settings, including
confined local inheritance, source includes and project references. Unsupported
or ambiguous layouts (including package-based/external inheritance, nested Git
repositories and incompatible referenced build projects) are refused. Discovery
ignores unrelated symbolic links without following them, including documentation
links. Linked or escaping configurations, references, generated declarations and
required integration directories are refused before authentication or writes;
local failures identify the path and condition. Access, parsing, transport or
write failures preserve the prior complete declaration and project configuration. Generation is serialized within
the worktree; after a process interruption, check that no generation is running
before removing a stale .os-data-schema.lock directory and refreshing again.
Local apps
From anywhere inside the app’s Git worktree, run:
os app launch <name>While editing the interface, use os app dev <name> instead. It runs the app's
existing Vite-compatible npm run dev script and keeps one OS host and browser
origin alive while HMR updates the interface. There is no production build on
this path. Install development dependencies first. App declaration and routine
definition changes require a restart; normal interface edits do not.
For ordinary app development, that command uses your installed CLI and the app's installed SDK. To test unreleased OS changes, first build the CLI and SDK in the OS checkout:
cd /path/to/os
bun run --filter @oddington/cli buildThen run from the customer repository:
os app dev billing --local-os /path/to/osThis explicitly runs that checkout's built CLI and supplies its built browser
SDK to the app's Vite server. Both selected paths are shown on stderr. Missing
builds stop with build guidance; no package is installed or substituted. The
app's existing Vite config/plugins still run. A temporary config override and
separate dependency cache last only for this invocation; customer dependencies,
lockfiles and authored config are preserved. Existing login and backend services
are used normally. Interface edits keep HMR; OS CLI/SDK edits require rebuilding
OS and restarting this command. Stop and omit --local-os to return to ordinary
installed packages. This flag selects local packages, not a local backend.
Until your installed CLI supports this option, invoke the built CLI directly from the customer repository:
node /path/to/os/apps/cli/dist/os.js app dev billing --local-os /path/to/osBoth commands print a clean local address you can open in multiple browsers simultaneously, including a chat browser that can reach this machine. Starting the host trusts this workstation's loopback, including other local processes and OS users able to connect. CLI credentials remain server-side. Each browser has independent connection proofs; renewal in one does not disconnect another. Stopping the command or signing out of the local app ends host access for all.
Launch reads exactly apps/<name>/app.json, resolves one member workspace from
the worktree's stable source identity, runs the colocated package’s fixed npm run build, and
opens its confined public interface in your ordinary browser. Prepare the app’s
documented dependencies separately; launch never installs them. Git, npm and
the required Node version must be available. Missing declarations, dependencies,
build output or membership stop launch with setup guidance.
The declaration contains only stable app scope. For example:
{
"name": "accounting",
"repositories": ["general"],
"routines": ["reconcile-records"]
}Definitions are discovered by exported ID in confined routines/*/routine.ts,
independently of directory names. os app build <name> and launch use the same
preparation, generate the ignored .os/app.d.ts and app-descriptor.json pair,
then build fixed dist/index.html. Types infer input/defaults, result and step
detail from the definition. Browser assets embed only the safe structural
descriptor, never access declarations, source imports or live availability.
Unsupported input or invalid generation fails preparation without exposing a
partial pair. An app without routine operations uses "routines": [].
The command prints the clean assigned IPv4 loopback URL and owns the host until
Ctrl-C or termination. No root, workspace, command or port override is accepted;
os app run is unsupported. Browser authority is separate, origin-scoped and
bounded to ten minutes per issued credential or the checked access session's
earlier expiry window. Before expiry the SDK proactively asks only that exact
Host to renew. The Host silently refreshes the securely stored CLI login and
confirms the same member, app, workspace and complete repository scope before
atomically rotating the browser credential and bounded deadline. CLI credentials
never enter browser state, and renewal never opens a login flow.
Local sign-out leaves CLI sign-in intact. Removing CLI storage alone leaves an
established unexpired app session intact but prevents renewal. Protected requests
check retained local authority; hosted identity and repository bindings are
observed together in the background without extending expiry. Confirmed scope
or target changes end the session; a same-target published-version advance
atomically replaces the affected index reader. Temporary background or renewal
failures preserve only current bounded access and never extend its deadline.
The SDK retries renewal independently of Data reads. Confirmed renewal denial and Host
shutdown end it immediately. An opener
failure stops the host and its development server.
Launch supplies authenticated context and unchanged interface assets.
app.listRoutines() preserves declaration order, including unavailable entries.
app.routines[id].describe() combines structural metadata with current readiness
and read-only connection availability; starts recheck current source and complete
authority. Defaulted run() and authorized OS File descriptors use the same
local runner as CLI. There is no app-specific child lifecycle.
Run handles expose current(), connected watch() and retained step(id).
Local summaries retain exact Data paths, safe live effects and Git provenance on
success and failure. There is no subsequent Run publication. Routine runs.list()
returns at most 20 recent summaries; runs.get(id) opens retained facts under the
established local session. Older Runs are not pruned. Interrupted execution has
no restart recovery.
os connection list --workspace <workspace> --all
os connection inspect --workspace <workspace> --id <connection-id>The shorter list command shows the enabled Gmail and Airtable connections
available to the signed-in person.
Create a connection
GitHub connections use an installed GitHub App:
os connection create github \
--workspace <workspace> \
--name <name> \
--account <github-account>A GitHub repository is then added as a target:
os connection target add \
--workspace <workspace> \
--id <connection-id> \
--repository <repository-name> \
--github-repository <github-repository> \
--target-ref refs/heads/mainGmail and Airtable credentials are supplied through standard input so they do not appear in shell history:
credential-source | os connection create gmail \
--workspace <workspace> \
--name invoices \
--account-email [email protected] \
--scopes https://www.googleapis.com/auth/gmail.readonly \
--credentials-stdincredential-source | os connection create airtable \
--workspace <workspace> \
--name project-management \
--base <base-id> \
--credentials-stdinHere, credential-source means an owner-controlled command that writes the
credential JSON to standard output. It is not an OS command.
Gmail expects:
{"clientId":"<client-id>","clientSecret":"<client-secret>","refreshToken":"<refresh-token>"}Airtable expects:
{"accessToken":"<personal-access-token>"}Creating a connection verifies access before saving it. It does not grant anyone permission to use it.
Outlook and SharePoint are separate connections for one mailbox or site. Both require tenant and application IDs and protected credential JSON from stdin:
credential-source | os connection create outlook \
--workspace <workspace> \
--name accounts-payable \
--tenant-id <tenant-id> \
--client-id <client-id> \
--mailbox-email [email protected] \
--access read \
--credentials-stdin
credential-source | os connection create sharepoint \
--workspace <workspace> \
--name operations \
--tenant-id <tenant-id> \
--client-id <client-id> \
--site-url https://example.sharepoint.com/sites/Operations \
--access read-write \
--credentials-stdinEach expects an object containing only clientSecret. The entire UTF-8 stdin
input must fit within 4,096 bytes. The secret never belongs in command arguments.
--access is optional nonempty descriptive metadata for either provider.
Supplied descriptions, including existing read and read-write values, are
preserved. They do not restrict or verify Microsoft permissions and are omitted
from safe output when absent. This metadata change requires no credential re-entry.
OS resolves the SharePoint site ID during verification; callers cannot supply it.
The Entra application's Microsoft permissions and site or mailbox grants must
already be configured upstream. Routine code owns ordinary Microsoft Graph
operations; OS does not provide an operations SDK or SharePoint File storage.
Test a connection
os connection test --workspace <workspace> --id <connection-id>For GitHub, a repository target can also be tested:
os connection test \
--workspace <workspace> \
--id <connection-id> \
--repository <repository-name>Gmail verification checks the configured account and scopes without reading messages. Airtable verification makes a read-only schema request; it does not prove that the credential can write records.
Outlook testing reads only the configured mailbox's inbox folder identity,
without reading messages or attachments. SharePoint testing resolves the site
URL to its saved ID and reads that site. Success establishes readable access
only, including when the access description is read-write; it does not prove write
access or the absence of broader Microsoft access. Inspect shows the complete
public configuration, including the verified SharePoint site ID.
Grant and use a connection
Use os whoami to find your user ID, then grant access explicitly:
os connection grant \
--workspace <workspace> \
--id <connection-id> \
--user <user-id>
os run \
--workspace <workspace> \
--connection gmail/invoices \
--exec node routine.jsUse --runtime <runtime-id> instead of --user for a remote runtime. Remote
runs use os run --remote and cannot fall back to a person's login.
Revoke future use with:
os connection revoke \
--workspace <workspace> \
--id <connection-id> \
--user <user-id>Revoking a grant prevents new credentials from being issued. It cannot recall a credential that has already been issued to a running process.
Rotate or disconnect
To rotate Gmail or Airtable credentials, repeat the matching create command
with the same name and external account or base. The new credentials are tested
before replacing the old ones.
For Outlook or SharePoint, repeat the matching create command with the same
provider and name and fresh credential stdin. Successful verification replaces
the credential while preserving the connection ID and history; failure leaves
the prior connection intact. Disconnect removes that connection's protected
credential without changing Microsoft permissions or other connections using
the same secret. Reconnecting requires fresh verified credentials.
To stop new use of a connection:
os connection disconnect \
--workspace <workspace> \
--id <connection-id> \
--yesDisconnecting preserves the connection's history. For GitHub, it does not uninstall the GitHub App.
Credential safety
Never put credentials in command arguments, logs, or issues. inspect, list,
and other CLI output never reveal saved credentials.
os run passes a short-lived credential to the child process in
OS_CONNECTION_CREDENTIAL. Only run trusted code, and do not print or
retain that value.
Add a routine
Inside your existing workspace repository, choose a routine name:
os routine add invoice-summaryThis creates routines/invoice-summary/routine.ts and its package file, installs
dependencies without package scripts, and validates the new definition. The
starter returns Hello world; edit it to do your work. Existing files and the
workspace SDK version are preserved. Names use 1–63 lowercase letters, numbers
or hyphens, starting with a letter or number; reserved names are rejected.
The command finishes by showing os routine run invoice-summary. It does not
run or deploy the routine. OS sign-in and workspace access are checked when you
run it. If installation fails, your new files stay in place with exact recovery
commands. This command requires an existing routines/package.json workspace;
it does not create a repository or set up a new workspace.
Check routine requirements
os routine check invoice-import
os routine check invoice-import --jsonChecks the frozen authored definition, local runtimes, configured workspace
capabilities and declared local Claude readiness. No business steps run, no
provider operations or model requests are made, and no Run, first-use routine
bookkeeping, Records, Files, Inbox items or publication intent are created.
OS login and readiness metadata still require normal host access. Failure
reports the same typed setup issues and recovery commands as pre-run checks;
JSON includes status, stepsRan: false, and any error/issues. A check does not
reserve access: running later checks current requirements again.
Deploy routines
From the customer Git worktree, a signed-in workspace member can run:
os routine deploy invoice-import
os routine deploy invoice-import --yes --jsonDeploy selects the defineRoutine export with that canonical ID from conventional
routines/<directory>/routine.ts files. It packages the complete definition,
including ordered steps and every declared response handler, reachable source and
frozen dependencies. The directory can have a different name. No routine.json,
installation or enablement is required. Requested access is a maximum intersected
with configured workspace capabilities. Source identity and signed-in membership
select the workspace; callers cannot override authority.
Before upload, the command shows the routine ID, requested access, named
connections and stable handler IDs. Declining writes nothing. Noninteractive
use requires --yes; it supplies consent and leaves every validation intact.
Build/startup failure preserves the previous current deployment. Deploying never
executes business code or publishes an Ask.
Modern layout uses one Bun workspace and lock under routines/, shared authoring
tools at its root, and a private ESM manifest per selected routine. For example,
dependency-free Ghost still has routines/ghost/package.json:
{ "name": "ghost", "private": true, "type": "module" }Include ghost in the root workspace membership and keep one frozen
routines/bun.lock. OS packages only Ghost's reachable source, declared assets
and frozen runtime graph; unrelated tooling and untouched legacy routines do
not enlarge or invalidate it. The empty graph needs no customer dependency
installation during deployment. Supported runtime packages are frozen registry
imports; customer scripts, build hooks, aliases and SDK replacement are refused.
See frozen source validation for the bounded
import and dependency contract.
The command reports bounded deployment facts and safe failures. JSON stdout is one outcome; review and progress use stderr. Closing the CLI stops inspection while accepted OS work continues. A later successful deploy replaces the single current unit for new remote Runs and newly accepted responses. V0 exposes no rollback or historical code selection. The command accepts no commit, module, image, command, credential or workspace selector. JSON returns product facts without progress prose and uses the same exit status as human output.
Publish asks and digests
A signed-in workspace member can publish an initial ask whose Questions have no handlers, or a digest, with explicit recipients. A trusted routine run uses the same boundary and records routine/run provenance. Questions with customer-owned handlers remain routine-authored and require that routine's compatible active deployment. A small registry of OS-shipped smoke handlers can instead be used directly, without a routine or repository. Items retain Rich text, Record, Records, Files, and Digest-only Asks Blocks, with optional registered panel Views. CSV, PDF and other Inbox files share one retention path. Self-contained common record-table Views omit repository, commit and row versions; explicit repository captures retain same-workspace repository binding and recipient access checks. Workspace membership provides repository access; there is no per-person repository ACL. Editors, unregistered views, prerequisites, legacy record-picker formats and direct member revisions remain deferred and reject explicitly.
os ask create --file <json> --request-key <key> --json
os digest create --file <json> --request-key <key> --dry-run --jsonThe JSON definition supplies title, description, optional questions (ask), and
explicit person IDs. --dry-run checks current membership, people, item and
repository access, and any executable handlers without publishing. Retry an
uncertain acknowledgement with the same key and unchanged content as the same
member or routine run.
TypeScript publishers install @oddington/sdk and import the authoring helpers
from its package root:
import { data, defineHandler, resolveAsk, routinePublicationClient } from '@oddington/sdk';
const inbox = routinePublicationClient();Trusted launches supply workspace and capability. A host publishing
as a member supplies routinePublicationClient({ workspace, request })
with its authorized member transport. CLI publication commands supply the CLI's
signed-in request themselves. Response modules export a handlers array and
perform no publishing on import. Accepted answers durably queue managed handlers
without a browser start request. Handlers use the same Data API as routines,
with current hosted reads and direct field updates. Digests have personal
Mark as read receipts.
Routines and handlers can assign known fields without reading first:
const repository = data.repository('general');
const changes = repository.changes();
changes.update('invoices/one', { status: 'ready' });
await changes.apply();Read through repository.records.read(refs) when business logic needs current
values. Handlers use await changes.publish() to confirm shared changes in Git.
Local Runs read current checkout Records and leave completed Data writes as ordinary uncommitted files for review. Hosted effects complete during the call.
The human completion summary groups paths under Created, Updated and Deleted, counting each file once across the Run. Repeated updates to a new file leave it Created. Exact-byte restorations appear under Unchanged, and temporary creations that were deleted are listed separately. The comparison spans the Run, including all steps; JSON activity still retains the completed operations.
Review new files in your editor; Git diffs show edits to tracked files. Completion calls out saved files ignored by Git and suggests correcting the ignore rule if they should be committed. It does not edit ignore rules, force-add files or alter the index. Stage suggestions exclude ignored files and unchanged restorations; partial-write recovery keeps its explicit saved/failed/unattempted guidance.
OS intersects the routine's declared access with configured workspace
capabilities. A new routine runs locally from a customer branch without
registration, a merge to main, or deployment. Write the canonical id as a literal
string directly in defineRoutine, such as id: 'invoice-import'; use that same
ID in the command. OS reads the declared IDs before preparing only the selected
routine. Named connections
must already be configured in the workspace; declaring a name does not create it:
os routine run invoice-import --input input.json
os routine run invoice-import --input - --jsonIf an existing local draft is missing its package file or dependency setup,
an interactive run can offer to create the minimal package file and install
dependencies in routines/, without running package scripts. It preserves
existing authored files and the declared SDK version. After validating the
definition, it prints the exact original run command; setup does not start the
routine automatically. JSON, non-interactive, remote, help and check commands
never perform this setup. Ambiguous drafts and undeclared runtime dependencies
require an explicit authored fix rather than a guessed package file.
OS resolves the worktree's stable source identity and the canonical defineRoutine
export in routine.ts. JSON input is validated before business work; its file/stdin
path is transport, not business input. Omitted input uses the definition's default,
when supplied. Each JSON value is bounded to 256 KiB. Running a routine does not
deploy it or its handlers.
To execute the active deployed version on OS, use:
os routine run invoice-import --remote --input input.json
os routine run invoice-import --remote --input - --jsonRemote start reads only worktree identity and business input. It neither uploads
local source nor creates a local captured change set. It reports the accepted
Run's ID, ordered steps, safe summaries and terminal result or failure through the
shared Run handle. Remote Runs omit local activity; their authorized OS changes
apply during execution. Input/defaults and access come from the deployed definition,
and later activation cannot change a started invocation's code. Retained step
detail, results and newest-first recent history use the same SDK handle contracts.
When a running step reports explicit progress, the interactive terminal updates
that step's existing spinner in place with completed/total; it does not print a
new line for every update. JSON retains the current progress with the step.
The command executes once and accepts no workspace, version, command, environment
or authority override.
Remote start responses use the standard Server-Timing header to separate
identity, aggregate database and Workflow dispatch time. Private run.start
events retain the safe request trace, caller path (app, api or cli), status,
total/dispatch time and per-upstream call counts/time. They never retain routine
input, source contents, credentials or database responses. Observability failure
cannot change start acceptance or execution.
Before local execution, the command names declared connections and explains that local filesystem changes, model calls and provider effects happen immediately. Only Record, File, Ask and Digest changes stay local for review. The child receives temporary scoped authority for the accepted execution.
Completed Data writes stay in the checkout, while File uploads and Inbox calls
are live. The terminal report and retained summary show exact local paths and
safe hosted references on success and failure. There is no later Run publication.
execution.json retains private bounded diagnostics; run.json retains the safe
summary and separate files retain requested step data.
--json emits one value on stdout; notices use stderr and child logs stay in
Run evidence. It includes runId, state, placement, optional declared result,
and the retained run summary including local activity.
Data collection, selection and search calls read the schema-governed Markdown currently present in declared local repositories. Later calls see completed writes and external edits. Authentication and named provider connections retain their normal network and authority requirements; local execution may contact OS or providers.
For modern local execution, install the workspace separately with Bun 1.3.14.
For a failed local Run, the terminal and JSON result identify its failed step
and known SDK error. Device-local ~/.os/<workspace>/runs/<run-id>/execution.json
retains redacted exception messages, stacks, causes and failed OS operations.
Those details are not returned through app summaries. Use SDK 0.1.51 and CLI
0.1.85 for this evidence; updating only the CLI does not replace the SDK frozen
into a customer routine or an earlier saved Run.
AI structured output is validated against the routine's requested schema.
generateObject returns a typed object or throws a catchable AIError
with a clear validation reason and optional original answer text. A routine can
inspect the answer, apply its own recovery policy and continue without making
the whole Run fail. An uncaught routine or step exception still fails execution.
Local execution.json keeps the last 32 rejected answers, including caught
failures in successful Runs. Failed Runs additionally keep the last eight final
answers. Each private sample is credential-redacted, bounded to 8 KiB, and marked
when truncated; rejected/retained/omitted counts are explicit. Compact per-call
metadata remains available when a sample was omitted.
Inspect one local Run using the active workspace and the login that started it:
os routine inspect <run-id> --ai
os routine inspect <run-id> --ai --json
os routine inspect <run-id> --ai --show-output 7Default output includes call, step, operation, outcome, safe validation reason,
answer size and sample availability. Answer text requires --show-output <call>
even with --json. The explicit display shows only that redacted local sample.
Older Runs may lack validation reasons or omission accounting (omitted: null
in JSON); missing samples
cannot be reconstructed. Inspection does not resume work or call a model.
AIError is the public SDK name; RoutineAIError remains the same constructor
as a compatibility alias. Customer catches should preserve safe code/detail/message
and item identity, keeping text private. CLI evidence correlates run/step/call;
there is no direct customer-item-to-call link in this slice. The SDK example
shows a typed catch. Shared summaries and ordinary command JSON exclude answer
samples, prompts, reasoning and provider logs.
Keep an exact stable @oddington/sdk 0.1.x patch through 0.1.52 in root
devDependencies, with its declaration, lock and installed package agreeing.
Use compatible Node, the selected private ESM manifest and the shared frozen
lock. OS verifies only the selected installed graph, stages its confined bytes
and builds the same
executable unit used by deployment, preserving declared assets and SDK ownership.
Missing build tools, incompatible SDK, invalid metadata or stale/unavailable
packages report unavailable before business work. Local Python dependency
provisioning is unsupported. Launch never repairs the workspace or falls back
from a broken modern manifest to legacy execution.
Global npm Bun is supported: npm install --global [email protected]. Add the global
executable directory to PATH. OS excludes executables owned by the customer
checkout, while accepting host installations whose paths contain node_modules.
Routine requirement failures name missing workspace resources and local AI
setup together before any business step starts. For example, a declared
dropbox/documents with no workspace connection is reported as a missing
connection, with the Dropbox connection setup command and a quoted placeholder
for the existing base folder's path inside Dropbox. The routine's Dropbox paths
are relative to that folder. Inspect all configured connections with
os connection list --workspace <slug> --all.
Local AI supports signed-in Claude Code 2.1.259 and newer. An older unsupported
version explains the required feature and prints claude update; a missing
login prints claude auth login. Settings-policy failures point to claude
doctor. Readiness never sends documents or makes a model request.
Reuse ordinary customer source through relative imports from routines/lib/,
without a package manifest in lib/. Each consuming routine declares any
runtime packages that shared code imports. Importing another routine's package
is rejected because its dependency boundary differs; copying its steps is not
required.
Business steps can also be tested with ordinary fixture inputs in the customer repository. A real local OS Run uses the configured workspace connections; fixtures do not require registering or deploying the routine.
Routines without a selected package manifest retain the existing legacy path:
native Node TypeScript stripping, a module routines/package.json, a matching
engines.node, and an installed SDK. A workspace root alone does not cut over
other routines or _runtime. Both paths freeze reachable source before
inspection, up to 20,000 files / 256 MiB. Unrelated routines, historical output,
unused dependencies, caches and logs are excluded. Reachable symlinks, native
addons and computed loaders/assets refuse; execution never uses changing checkout code.
No dependency installation or package script runs. Run state is bounded to
64 MiB; reports and redacted output each retain at most 10 MiB. Launcher ownership
renews every 20 seconds with a 90-second lease; execution retains the existing
15-minute capability bound.
routine prepare, routine publish, run --local, executable/argv selection,
and routine workspace, name and working-root flags are retired. os run --connection
remains the separate ad hoc command.
Each current local Data operation reads committed, staged, modified and untracked
schema-governed Markdown. changes.validate() writes nothing. changes.apply()
immediately writes the requested local changes; there is no later Run
publication or prepared repository state. It validates the touched Records,
paths, authored links and captured versions without auditing unrelated Record
contents, then applies complete files atomically, one at a time. It retains
completed paths if a later filesystem operation fails; no rollback or replay occurs.
Subsequent reads observe completed writes and external edits. Current local Data
needs no published base, repository-context request or hosted index.
Files default to local storage. Explicit remote creation/upload completes live;
Inbox uploads referenced local Files before returning real hosted item identities.
Run summaries and CLI output distinguish live effects from exact local Data paths,
including safe write failures, with path-scoped Git review and staging commands.
Steps, results/failures and activity remain under ~/.os/<workspace>/runs/<id>.
Installed write grants authorize Data changes() for routine Record creates,
updates and deletions. The runtime owns retry identity. routinePublicationClient()
publishes Inbox work and has no Record methods. Managed Response handlers retain
their separately injected repository capability and durable execution identity;
handler writes use their installation independently of the responder's identity.
Customer executable handlers still require an approved managed deployment.
Add a file through shared Files before putting it on an ask or digest:
os files add --file ./output/september.csv --path Statements --json
os ask create --file ./ask.json --request-key september-review --jsonThe signed-in session selects the active workspace. --file names a regular
file on this computer (relative to the current directory or absolute); --path
is its managed parent and defaults to Files. --storage local|remote defaults
to local, and --name may override the saved filename. JSON output is one shared
File descriptor. There is no File delete command or working-root option.
Supply that exact descriptor in a Files block in the Ask or Digest JSON. The
command uploads a local File before publication, retaining its ID, managed path,
hash and enrichments; existing remote Files are reused. --dry-run checks without
uploading or publishing. Member publication commands accept no workspace selector
or attachment paths.
Routines use files.create({ file, name, path }) and
fileBlock({ label, files }) from @oddington/sdk; Inbox has no separate intake
API. Blocks retain safe immutable File metadata. Current workspace Files access
owns bytes, while recipient access governs the Inbox item. CSV preview remains a
consumer concern; unsupported files remain downloadable.
Resolution is an explicit guarded business outcome from a handler or the original publishing routine.
Package boundary
@oddington/cli contains the os executable and depends on the compatible
published @oddington/sdk. It owns login, session storage, refresh and launch
orchestration. Importable authoring, file and repository capabilities belong to
the SDK; the former @oddington/cli/inbox export is removed. Both packages require
Node.js 22.12.0 or later, and installation grants no authority.
The managed response runner supplies its own SDK to approved handlers. Customer manifests cannot replace it, and a module importing only the SDK needs no customer dependency installation. Internal operator commands and service credentials are excluded from both public packages.
