@onesi/genesis-cli 0.1.13 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +29 -2
  2. package/dist/beacons.js +68 -0
  3. package/dist/dials.js +7 -4
  4. package/dist/fleets.js +30 -0
  5. package/dist/flocks.d.ts +7 -1
  6. package/dist/flocks.js +257 -3
  7. package/dist/henry.d.ts +5 -1
  8. package/dist/henry.js +60 -1
  9. package/dist/hives-coverage.d.ts +12 -0
  10. package/dist/hives-coverage.js +116 -0
  11. package/dist/hives-node-transport.js +3 -0
  12. package/dist/hives-operations.d.ts +1 -0
  13. package/dist/hives-operations.js +2 -0
  14. package/dist/hives-platform.d.ts +13 -0
  15. package/dist/hives-platform.js +133 -0
  16. package/dist/iam-directory.d.ts +15 -0
  17. package/dist/iam-directory.js +71 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +255 -23
  20. package/dist/minds.d.ts +1 -1
  21. package/dist/minds.js +37 -2
  22. package/dist/onboard/art.d.ts +50 -0
  23. package/dist/onboard/art.js +139 -0
  24. package/dist/onboard/brand.d.ts +56 -0
  25. package/dist/onboard/brand.js +134 -0
  26. package/dist/onboard/caps.d.ts +90 -0
  27. package/dist/onboard/caps.js +232 -0
  28. package/dist/onboard/encode.d.ts +23 -0
  29. package/dist/onboard/encode.js +235 -0
  30. package/dist/onboard/eye.d.ts +103 -0
  31. package/dist/onboard/eye.js +147 -0
  32. package/dist/onboard/index.d.ts +61 -0
  33. package/dist/onboard/index.js +562 -0
  34. package/dist/onboard/mcp.d.ts +35 -0
  35. package/dist/onboard/mcp.js +118 -0
  36. package/dist/onboard/raster.d.ts +43 -0
  37. package/dist/onboard/raster.js +253 -0
  38. package/dist/onboard/sound.d.ts +2 -0
  39. package/dist/onboard/sound.js +54 -0
  40. package/dist/onboard/state.d.ts +24 -0
  41. package/dist/onboard/state.js +48 -0
  42. package/dist/onboard/term.d.ts +69 -0
  43. package/dist/onboard/term.js +292 -0
  44. package/dist/onboard/ui.d.ts +97 -0
  45. package/dist/onboard/ui.js +615 -0
  46. package/dist/orgs.d.ts +16 -0
  47. package/dist/orgs.js +89 -0
  48. package/dist/products.d.ts +6 -2
  49. package/dist/products.js +238 -12
  50. package/dist/relays-commands/certificates.d.ts +3 -0
  51. package/dist/relays-commands/certificates.js +20 -0
  52. package/dist/relays-commands/domains.d.ts +3 -0
  53. package/dist/relays-commands/domains.js +23 -0
  54. package/dist/relays-commands/index.d.ts +4 -0
  55. package/dist/relays-commands/index.js +10 -0
  56. package/dist/relays-commands/monitoring.d.ts +3 -0
  57. package/dist/relays-commands/monitoring.js +41 -0
  58. package/dist/relays-commands/telemetry.d.ts +3 -0
  59. package/dist/relays-commands/telemetry.js +28 -0
  60. package/dist/relays-commands/zones.d.ts +5 -0
  61. package/dist/relays-commands/zones.js +9 -0
  62. package/dist/runner.js +2 -0
  63. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { registerMinds } from './minds.js';
2
2
  import { registerRelaysEnrollment } from './relays-enrollment.js';
3
+ import { RELAYS_COMMANDS } from './relays-commands/index.js';
3
4
  import { registerBeacons } from './beacons.js';
4
5
  import { registerDials } from './dials.js';
5
- import { registerProducts } from './products.js';
6
+ import { registerIamDirectory } from './iam-directory.js';
7
+ import { registerOperationCommands, registerProducts } from './products.js';
8
+ import { registerOrgs } from './orgs.js';
6
9
  import { Command, CommanderError } from 'commander';
7
10
  import { Genesis, GenesisError, errorCatalog, errorOutcome, isMindsOperation, HivesOperationError, HivesQueueOperationError, MindsAgentError, MindsOwnerError, hivesResponseBytes, MAX_FUNCTION_BODY_BYTES, MAX_FUNCTION_JSON_BYTES, DEFAULT_BASE_URL, requireSecureUrl } from '@onesi/genesis';
8
11
  import { authConfig, completeLogin, selectSavedContext, savedLoginProvider, startLogin } from './auth.js';
@@ -16,6 +19,7 @@ import { registerFlocks } from './flocks.js';
16
19
  import { registerRunner } from './runner.js';
17
20
  import { createHivesNodeTransport } from './hives-node-transport.js';
18
21
  import { registerHivesOperations } from './hives-operations.js';
22
+ import { registerHivesCoverage } from './hives-coverage.js';
19
23
  import { readFunctionFile, readFunctionJson, writeFunctionOutput } from './function-input.js';
20
24
  import { basename } from 'node:path';
21
25
  import { readWasmFile } from './wasm-input.js';
@@ -36,7 +40,7 @@ export function buildProgram(deps = {}) {
36
40
  const auth = { ...authConfig(env, f), sleep: deps.sleep };
37
41
  const program = new Command('genesis')
38
42
  .description('One CLI for every Genesis service.')
39
- .version('0.1.13')
43
+ .version('0.2.0')
40
44
  .option('--json', 'machine-readable JSON output')
41
45
  .option('--api-key <key>', 'use this API key (default: GENESIS_API_KEY, then your `genesis login`)')
42
46
  .option('--base-url <url>', 'gateway URL (default: GENESIS_BASE_URL or https://os.one.ml/api)')
@@ -52,7 +56,7 @@ export function buildProgram(deps = {}) {
52
56
  const opts = {
53
57
  apiKey: o.apiKey ?? env.GENESIS_API_KEY, token: env.GENESIS_TOKEN,
54
58
  baseUrl: o.baseUrl ?? env.GENESIS_BASE_URL, routing: o.direct || env.GENESIS_ROUTING === 'direct' ? 'direct' : 'gateway',
55
- fetch: f, userAgent: 'genesis-cli/0.1.13',
59
+ fetch: f, userAgent: 'genesis-cli/0.2.0',
56
60
  credentials: store ? savedLoginProvider(auth, store) : undefined,
57
61
  };
58
62
  return new Genesis(hivesTransport ? { ...opts, fetch: createHivesNodeTransport(opts, deps.signal) } : opts);
@@ -82,23 +86,102 @@ export function buildProgram(deps = {}) {
82
86
  return (await gen.whoami()).org;
83
87
  };
84
88
  const silos = program.command('silos').description('Serverless PostgreSQL');
85
- const silosDatabases = silos.command('databases').description('Read existing owned database metadata; no provisioning');
86
- silosDatabases.command('list <project_id>').action(async (id) => out.log(JSON.stringify(await (await client()).silos.databases.list(id), null, 2)));
87
- silosDatabases.command('get <project_id> <database_id>').action(async (project, database) => out.log(JSON.stringify(await (await client()).silos.databases.get(project, database), null, 2)));
88
- const silosProjects = silos.command('projects').description('Projects in your verified organization');
89
- silosProjects.command('list').action(async () => out.log(JSON.stringify(await (await client()).silos.projects.list(), null, 2)));
90
- silosProjects.command('get <project_id>').action(async (id) => out.log(JSON.stringify(await (await client()).silos.projects.get(id), null, 2)));
91
- silosProjects.command('create').requiredOption('--name <name>', 'Project name').requiredOption('--slug <slug>', 'Unique project slug')
92
- .action(async (options) => out.log(JSON.stringify(await (await client()).silos.projects.create({ name: options.name, slug: options.slug }), null, 2)));
89
+ const silosInstances = silos.command('instances').description('Database instances in the selected Genesis project (genesis select)');
90
+ silosInstances.command('list').action(async () => out.log(JSON.stringify(await (await client()).silos.instances.list(), null, 2)));
91
+ silosInstances.command('get <instance_id>').action(async (id) => out.log(JSON.stringify(await (await client()).silos.instances.get(id), null, 2)));
92
+ silosInstances.command('create').requiredOption('--name <name>', 'Instance name (lowercase, 1-63 characters)')
93
+ .action(async (options) => out.log(JSON.stringify(await (await client()).silos.instances.create({ name: options.name }), null, 2)));
94
+ const silosJson = (value) => out.log(JSON.stringify(value, null, 2));
95
+ silosInstances.command('rename <instance_id>').requiredOption('--name <name>', 'New instance name (lowercase, 1-63 characters)')
96
+ .action(async (id, options) => silosJson(await (await client()).silos.instances.update(id, { name: options.name })));
97
+ silosInstances.command('delete <instance_id>').description('Delete an instance and stop it; its name is free again at once')
98
+ .action(async (id) => silosJson((await (await client()).silos.instances.delete(id)) ?? { deleted: id }));
99
+ silosInstances.command('suspend <instance_id>').description('Checkpoint, then stop the instance')
100
+ .action(async (id) => silosJson(await (await client()).silos.instances.suspend(id)));
101
+ silosInstances.command('resume <instance_id>').description('Start the instance now')
102
+ .action(async (id) => silosJson(await (await client()).silos.instances.resume(id)));
103
+ silosInstances.command('query <instance_id>').description('Run one SQL statement (never replayed)')
104
+ .requiredOption('--sql <sql>', 'SQL statement').option('--params <json>', 'JSON array of positional parameters for $1, $2, …')
105
+ .option('--branch <branch_id>', 'run on this branch instead of the default branch')
106
+ .action(async (id, options) => {
107
+ let params;
108
+ if (options.params !== undefined) {
109
+ try {
110
+ params = JSON.parse(options.params);
111
+ }
112
+ catch {
113
+ params = undefined;
114
+ }
115
+ if (!Array.isArray(params))
116
+ throw new Error('--params must be a JSON array, for example \'[1,"a"]\'.');
117
+ }
118
+ silosJson(await (await client()).silos.instances.query(id, { sql: options.sql, ...(params ? { params } : {}), ...(options.branch ? { branchId: options.branch } : {}) }));
119
+ });
120
+ silosInstances.command('batch <instance_id>').description('Run up to 50 statements in one transaction (never replayed)')
121
+ .option('--file <path>', 'UTF-8 JSON file: {"statements":[{"sql":"…","params":[…]}]}').option('--stdin', 'read that JSON from stdin')
122
+ .action(async (id, o) => {
123
+ let body;
124
+ if (Boolean(o.file) === Boolean(o.stdin))
125
+ throw new Error('Choose exactly one input source: --file or --stdin.');
126
+ const text = await (deps.readSecretInput ?? readSecretInput)(o, 64 * 1024);
127
+ try {
128
+ body = JSON.parse(text);
129
+ }
130
+ catch {
131
+ throw new Error('Batch input must be JSON: {"statements":[{"sql":"…"}]}.');
132
+ }
133
+ silosJson(await (await client()).silos.instances.batch(id, body));
134
+ });
135
+ silosInstances.command('metrics <instance_id>').description('Live counters, storage and activity times')
136
+ .action(async (id) => silosJson(await (await client()).silos.instances.metrics(id)));
137
+ silosInstances.command('listen <instance_id>').description('Wait for NOTIFY on channels (up to --wait ms, at most 15000)')
138
+ .requiredOption('--channel <name...>', 'channel to listen on (repeatable, up to 10)').option('--wait <ms>', 'how long to wait (default 10000)')
139
+ .option('--branch <branch_id>', 'listen on this branch instead of the default branch')
140
+ .action(async (id, o) => {
141
+ const waitMs = o.wait === undefined ? undefined : Number(o.wait);
142
+ if (waitMs !== undefined && !(Number.isInteger(waitMs) && waitMs >= 0 && waitMs <= 15000))
143
+ throw new Error('--wait must be an integer from 0 to 15000.');
144
+ silosJson(await (await client()).silos.instances.listen(id, { channels: o.channel, ...(waitMs !== undefined ? { waitMs } : {}), ...(o.branch ? { branchId: o.branch } : {}) }));
145
+ });
146
+ silos.command('usage').description('Silos usage of the selected Genesis project, last 30 days')
147
+ .action(async () => silosJson(await (await client()).silos.usage()));
148
+ silos.command('regions').description('Regions Silos instances are created in')
149
+ .action(async () => silosJson(await (await client()).silos.regions.list()));
150
+ const silosBranches = silos.command('branches').description('Branches of a database instance');
151
+ silosBranches.command('list <instance_id>').action(async (id) => silosJson(await (await client()).silos.instances.branches.list(id)));
152
+ silosBranches.command('get <instance_id> <branch_id>').action(async (id, branch) => silosJson(await (await client()).silos.instances.branches.get(id, branch)));
153
+ silosBranches.command('create <instance_id>').description('Copy the latest checkpoint of the parent (default: the default branch)')
154
+ .requiredOption('--name <name>', 'Branch name (lowercase, 1-63 characters)').option('--parent <branch_id>', 'parent branch')
155
+ .action(async (id, o) => silosJson(await (await client()).silos.instances.branches.create(id, o.parent ? { name: o.name, parentBranchId: o.parent } : { name: o.name })));
156
+ silosBranches.command('rename <instance_id> <branch_id>').requiredOption('--name <name>', 'New branch name')
157
+ .action(async (id, branch, o) => silosJson(await (await client()).silos.instances.branches.update(id, branch, { name: o.name })));
158
+ silosBranches.command('delete <instance_id> <branch_id>').description('Delete a branch other than the default branch')
159
+ .action(async (id, branch) => silosJson((await (await client()).silos.instances.branches.delete(id, branch)) ?? { deleted: branch }));
160
+ silosBranches.command('restore <instance_id> <branch_id>').description('Replace the branch data with a stored snapshot')
161
+ .requiredOption('--snapshot <snapshot_id>', 'snapshot id from `genesis silos snapshots list`').option('--from <branch_id>', 'branch the snapshot was taken on (default: this branch)')
162
+ .action(async (id, branch, o) => silosJson(await (await client()).silos.instances.branches.restore(id, branch, o.from ? { snapshotId: o.snapshot, sourceBranchId: o.from } : { snapshotId: o.snapshot })));
163
+ const silosSnapshots = silos.command('snapshots').description('Stored snapshots of a database instance');
164
+ silosSnapshots.command('list <instance_id>').action(async (id) => silosJson(await (await client()).silos.instances.snapshots.list(id)));
165
+ silosSnapshots.command('create <instance_id>').description('Checkpoint a branch and store the snapshot').option('--branch <branch_id>', 'branch (default: the default branch)')
166
+ .action(async (id, o) => silosJson(await (await client()).silos.instances.snapshots.create(id, o.branch ? { branchId: o.branch } : {})));
93
167
  const traces = program.command('traces').description('Research and evidence');
94
168
  const tracesRuns = traces.command('runs').description('Research runs in your verified organization');
95
169
  tracesRuns.command('create').requiredOption('--type <type>', 'Run type, e.g. deep_research').requiredOption('--query <query>', 'Research question')
96
170
  .requiredOption('--idempotency-key <key>', 'Stable key; reuse it to replay instead of creating a second run')
97
- .action(async (o) => out.log(JSON.stringify(await (await client()).traces.runs.create({ type: o.type, query: o.query }, o.idempotencyKey), null, 2)));
171
+ .option('--max-sources <n>', 'Most sources the run may collect (1-100)')
172
+ .option('--include-domain <domain...>', 'Search only these domains, e.g. wikipedia.org')
173
+ .action(async (o) => {
174
+ const config = { ...(o.maxSources !== undefined ? { max_sources: positiveInteger(o.maxSources, '--max-sources', 100) } : {}), ...(o.includeDomain?.length ? { include_domains: o.includeDomain } : {}) };
175
+ out.log(JSON.stringify(await (await client()).traces.runs.create({ type: o.type, query: o.query, ...(Object.keys(config).length ? { config } : {}) }, o.idempotencyKey), null, 2));
176
+ });
98
177
  tracesRuns.command('list').action(async () => out.log(JSON.stringify(await (await client()).traces.runs.list(), null, 2)));
99
178
  tracesRuns.command('get <run_id>').action(async (id) => out.log(JSON.stringify(await (await client()).traces.runs.get(id), null, 2)));
100
179
  traces.command('search <query>').requiredOption('--idempotency-key <key>', 'Stable key; metered once')
101
- .action(async (query, o) => out.log(JSON.stringify(await (await client()).traces.search({ query, max_results: 3 }, o.idempotencyKey), null, 2)));
180
+ .option('--max-results <n>', 'Results to return (1-20, default 3)')
181
+ .option('--include-domain <domain...>', 'Search only these domains, e.g. wikipedia.org')
182
+ .action(async (query, o) => out.log(JSON.stringify(await (await client()).traces.search({
183
+ query, max_results: positiveInteger(o.maxResults, '--max-results', 20) ?? 3, ...(o.includeDomain?.length ? { include_domains: o.includeDomain } : {})
184
+ }, o.idempotencyKey), null, 2)));
102
185
  traces.command('usage').action(async () => out.log(JSON.stringify(await (await client()).traces.usage.events(), null, 2)));
103
186
  const blinds = program.command('blinds').description('Blinds credential custody');
104
187
  const show = async (call) => out.log(JSON.stringify(await call((await client()).blinds), null, 2));
@@ -115,7 +198,41 @@ export function buildProgram(deps = {}) {
115
198
  ...(o.domain ? { destination_domains: o.domain } : {}), ...(o.provider ? { provider: o.provider } : {}) }, key(o)));
116
199
  });
117
200
  credentials.command('revoke <variable>').option(...keyOption).action((variable, o) => show(b => b.credentials.revoke(variable, key(o))));
201
+ credentials.command('revoke-all').description('Revoke every credential in the selected organization (irreversible)')
202
+ .requiredOption('--yes', 'Confirm the bulk revoke').option(...keyOption).action((o) => show(b => b.credentials.revokeAll(key(o))));
118
203
  blinds.command('connections').description('Active OAuth connections (metadata only)').action(() => show(b => b.oauth.connections({ signal: deps.signal })));
204
+ const offsetInteger = (value) => {
205
+ if (value === undefined)
206
+ return undefined;
207
+ if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)))
208
+ throw new Error('--offset must be a non-negative integer.');
209
+ return Number(value);
210
+ };
211
+ const catalog = (o) => {
212
+ const limit = positiveInteger(o.limit, '--limit', 1000), offset = offsetInteger(o.offset);
213
+ return { ...(o.q ? { q: o.q } : {}), ...(o.category ? { category: o.category } : {}),
214
+ ...(limit !== undefined ? { limit } : {}), ...(offset !== undefined ? { offset } : {}) };
215
+ };
216
+ const providers = blinds.command('providers').description('Provider catalog: what a credential can be bound to');
217
+ providers.command('list').option('--q <text>', 'Match slug, name or description').option('--category <name>').option('--auth-mode <mode>', 'oauth2, api_key, ...')
218
+ .option('--oauth', 'Only providers that use OAuth 2.0 (cannot be combined with --auth-mode)').option('--limit <n>', 'Page size, 1 to 1000').option('--offset <n>')
219
+ .action((o) => {
220
+ if (o.oauth && o.authMode)
221
+ throw new Error('--oauth already selects auth mode oauth2; drop --auth-mode.');
222
+ const query = catalog(o);
223
+ return show(b => o.oauth ? b.oauth.providers(query, { signal: deps.signal })
224
+ : b.providers.list({ ...query, ...(o.authMode ? { auth_mode: o.authMode } : {}) }, { signal: deps.signal }));
225
+ });
226
+ providers.command('get <slug>').action((slug) => show(b => b.providers.get(slug, { signal: deps.signal })));
227
+ const settings = blinds.command('settings').description('Consent policy and notifications');
228
+ settings.command('get').action(() => show(b => b.settings.get({ signal: deps.signal })));
229
+ settings.command('set').description('Replace the settings: an omitted --email or --slack turns that notification off').requiredOption('--consent-policy <policy>', 'never, per_variable or per_delegate')
230
+ .option('--email', 'Email notifications on').option('--slack', 'Slack notifications on').option(...keyOption)
231
+ .action((o) => show(b => b.settings.update({ consent_policy: o.consentPolicy, email_notifications: !!o.email, slack_notifications: !!o.slack }, key(o))));
232
+ blinds.command('agents').description('Agents seen using Blinds, with request counts').action(() => show(b => b.agents({ signal: deps.signal })));
233
+ blinds.command('alerts').description('Failed or denied Blinds events').action(() => show(b => b.alerts({ signal: deps.signal })));
234
+ blinds.command('operation <operation_id>').description('Receipt for a mutation, by its Idempotency-Key')
235
+ .action((id) => show(b => b.operations.get(id, { signal: deps.signal })));
119
236
  const consent = blinds.command('consent').description('Agent consent requests and decisions');
120
237
  consent.command('pending').action(() => show(b => b.consent.pending({ signal: deps.signal })));
121
238
  consent.command('list').action(() => show(b => b.consent.records({ signal: deps.signal })));
@@ -141,7 +258,22 @@ export function buildProgram(deps = {}) {
141
258
  });
142
259
  }
143
260
  registerDials(program, { client, orgId, emit });
261
+ registerIamDirectory(program, { client, orgId, emit });
144
262
  registerProducts(program, { client, emit, signal: deps.signal });
263
+ registerOrgs(program, { client, emit, signal: deps.signal });
264
+ // Agent enrollment sends only the grant's enrollment key (mek_...), never this CLI's login or API key.
265
+ (program.commands.find(c => c.name() === 'iam').commands.find(c => c.name() === 'agents')).command('enroll')
266
+ .description('Enroll an agent under an agent enrollment grant (POST /v1/iam/agents/enroll); the credential secret is shown once')
267
+ .requiredOption('--body-file <path>', 'JSON request body (externalKey, displayName, mindId, permissions, ...)')
268
+ .option('--key-file <path>', 'file holding the enrollment key (mek_...)').option('--key-stdin', 'read the enrollment key from stdin')
269
+ .action(async (o) => {
270
+ const key = o.keyFile || o.keyStdin ? (await (deps.readSecretInput ?? readSecretInput)({ file: o.keyFile, stdin: o.keyStdin }, 1024)).trim() : env.GENESIS_ENROLLMENT_KEY;
271
+ if (!key)
272
+ throw new Error('Give the enrollment key with --key-file, --key-stdin or GENESIS_ENROLLMENT_KEY; it is never accepted as an argument.');
273
+ const body = await readFunctionJson(o.bodyFile, 16 * 1024);
274
+ const result = await new Genesis({ baseUrl: g().baseUrl ?? env.GENESIS_BASE_URL, fetch: f, userAgent: 'genesis-cli/0.1.13' }).iam.agents.enroll(key, body);
275
+ emit(result, () => JSON.stringify(result, null, 2));
276
+ });
145
277
  // ---- auth ------------------------------------------------------------
146
278
  program.command('login').description('Sign in with One in your browser').option('--no-browser', 'just print the link')
147
279
  .option('--device', 'sign in from a machine without a local browser: approve a code from any signed-in browser')
@@ -196,7 +328,8 @@ export function buildProgram(deps = {}) {
196
328
  emit({ user: saved.user, organization_id: saved.organization_id, verified: false, reason: e.message }, () => `${c.bold('User')} ${saved.user?.email ?? saved.user?.id}\n${c.bold('Org')} ${saved.organization_id ?? '-'}\n${c.yellow('!')} ${c.dim('From your saved login; One account verify unavailable: ' + e.message)}`);
197
329
  }
198
330
  });
199
- const orgs = program.command('orgs').description('Your organizations');
331
+ // `orgs usage|observations|billing|payment-methods|service` and the Orgs (legal entity) commands are registered by registerProducts and registerOrgs.
332
+ const orgs = (program.commands.find(command => command.name() === 'orgs') ?? program.command('orgs')).description('Your organizations, their usage and billing reads, and Orgs: legal entities, seats, resolutions, documents, filings, formation and the record');
200
333
  orgs.command('list', { isDefault: true }).description('Show your current organization').action(async () => {
201
334
  const s = await (await getStore()).get();
202
335
  if (!s)
@@ -247,7 +380,8 @@ export function buildProgram(deps = {}) {
247
380
  emit({ revoked: id }, () => `${c.green('✓')} Revoked ${id} ${c.dim('(takes effect within 60s)')}`);
248
381
  });
249
382
  // ---- foundry ---------------------------------------------------------
250
- const foundry = program.command('foundry').description('Foundry: models and inference');
383
+ // registerProducts (above) already added the generated `foundry` commands; the hand-written ones join them.
384
+ const foundry = program.commands.find(c => c.name() === 'foundry') ?? program.command('foundry').description('Foundry: models and inference');
251
385
  const foundryBilling = foundry.command('billing').description('Current organization’s native TEST billing; original One account human login required');
252
386
  foundryBilling.command('profile').action(async () => out.log(JSON.stringify(await (await client()).foundry.billing.profile({ signal: deps.signal }), null, 2)));
253
387
  foundryBilling.command('ensure').requiredOption('--operation-key <key>', 'Persisted operation key; reconcile unknown outcomes before retrying').action(async (o) => out.log(JSON.stringify(await (await client()).foundry.billing.ensureProfile(o.operationKey, { signal: deps.signal }), null, 2)));
@@ -285,7 +419,7 @@ export function buildProgram(deps = {}) {
285
419
  const hivesClient = () => client(true);
286
420
  // ---- hives -----------------------------------------------------------
287
421
  const hives = program.command('hives').description('Hives: projects, secrets, deployments, functions, schedules and activity');
288
- registerHivesOperations(hives, { client: hivesClient, emit, readRequest: deps.readHivesRequest });
422
+ registerHivesOperations(hives, { client: hivesClient, emit, readRequest: deps.readHivesRequest, line: (text) => out.log(text) });
289
423
  const deployments = hives.command('deployments').description('Real WASM artifacts and deployment lifecycle');
290
424
  const emitDeployment = (data) => emit(data, () => table(data && typeof data === 'object' && !Array.isArray(data) && !('data' in data) ? [data] : listOf(data), ['id', 'version', 'status', 'module_hash', 'created_at']));
291
425
  deployments.command('list <project_id>')
@@ -358,10 +492,10 @@ export function buildProgram(deps = {}) {
358
492
  const result = await (await hivesClient()).hives.functions.delete(project, id);
359
493
  emit(result, () => `Deleted function ${id}.`);
360
494
  });
361
- function invocationCommand(command, kind) {
495
+ function invocationCommand(command, kind, alias = false) {
362
496
  if (kind === 'functions')
363
497
  command.option('--idempotency-key <key>', 'Persisted original invocation key; required by Genesis funding. Reuse it after uncertainty.');
364
- command.description('Complete real guest execution; write exact bytes, including executed HTTP error bodies')
498
+ command.description(alias ? 'Compatibility alias of invoke (/invoke/async): the same completed guest response, no durable queue' : 'Complete real guest execution; write exact bytes, including executed HTTP error bodies')
365
499
  .option('--request-file <file>', 'JSON method/path/headers/body or body_base64')
366
500
  .option('--body-file <file>', 'Exact binary body (up to 10 MiB; serialized guest envelope may reach its cap earlier)')
367
501
  .option('--method <method>', 'guest HTTP method', 'POST').option('--path <path>', 'guest path including query', '/')
@@ -374,7 +508,8 @@ export function buildProgram(deps = {}) {
374
508
  let request = o.requestFile ? await readFunctionJson(o.requestFile, MAX_FUNCTION_JSON_BYTES) : { method: o.method, path: o.path };
375
509
  if (o.bodyFile)
376
510
  request.body_base64 = Buffer.from(await readFunctionFile(o.bodyFile, MAX_FUNCTION_BODY_BYTES)).toString('base64');
377
- const response = await (await hivesClient()).hives[kind].invoke(project, id, request, { idempotencyKey: o.idempotencyKey });
511
+ const hivesApi = (await hivesClient()).hives;
512
+ const response = alias ? await hivesApi.functions.invokeAsync(project, id, request, { idempotencyKey: o.idempotencyKey }) : await hivesApi[kind].invoke(project, id, request, { idempotencyKey: o.idempotencyKey });
378
513
  const bytes = await hivesResponseBytes(response);
379
514
  if (g().json)
380
515
  out.log(JSON.stringify({ status: response.status, headers: Object.fromEntries(response.headers), body_base64: Buffer.from(bytes).toString('base64') }));
@@ -389,6 +524,8 @@ export function buildProgram(deps = {}) {
389
524
  }
390
525
  invocationCommand(functions.command('invoke <project_id> <function_id>'), 'functions');
391
526
  invocationCommand(deployments.command('invoke <project_id> <deployment_id>'), 'deployments');
527
+ // The service's /invoke/async compatibility alias: the same completed guest response, no durable queue.
528
+ invocationCommand(functions.command('invoke-async <project_id> <function_id>'), 'functions', true);
392
529
  const operations = functions.command('operations').description('Read or release the original funding operation without replaying the guest');
393
530
  operations.command('get <project_id> <function_id> <operation_id>').action(async (project, fn, operation) => {
394
531
  const result = await (await hivesClient()).hives.functions.operations.get(project, fn, operation);
@@ -545,9 +682,12 @@ export function buildProgram(deps = {}) {
545
682
  page: positiveInteger(o.page, '--page', 10000), per_page: positiveInteger(o.perPage, '--per-page', 100) });
546
683
  emit(r, () => table(listOf(r), ['created_at', 'action', 'actor_id', 'resource_type', 'resource_name']));
547
684
  });
685
+ // Last: extends the hives groups defined above (flights, logs, env values, domain reads).
686
+ registerHivesCoverage(hives, { client: hivesClient, emit, readRequest: deps.readHivesRequest });
548
687
  // ---- relays ----------------------------------------------------------
549
688
  const relays = program.command('relays').description('Relays: DNS zones and records');
550
689
  registerRelaysEnrollment(relays, client, emit);
690
+ registerOperationCommands(relays, RELAYS_COMMANDS, { client, emit, signal: deps.signal });
551
691
  relays.command('zones').description('List DNS zones').option('--search <q>').action(async (o) => {
552
692
  const r = await (await client()).relays.zones.list(o.search ? { search: o.search } : {});
553
693
  emit(r, () => table((r.zones ?? []), ['name', 'type', 'record_count', 'dnssec_enabled', 'status']));
@@ -560,7 +700,58 @@ export function buildProgram(deps = {}) {
560
700
  const result = await (await client()).relays.zones.dnssec(zone);
561
701
  emit(result, () => JSON.stringify(result, null, 2));
562
702
  });
703
+ // Writes need relays:write and act on the selected project's existing Relays account.
704
+ const dnsInt = (value, option, minimum, maximum) => {
705
+ if (value === undefined)
706
+ return undefined;
707
+ if (!/^\d+$/.test(value) || Number(value) < minimum || Number(value) > maximum)
708
+ throw new Error(`${option} must be an integer from ${minimum} to ${maximum}.`);
709
+ return Number(value);
710
+ };
711
+ const recordId = (value) => dnsInt(value, '<record_id>', 1, Number.MAX_SAFE_INTEGER);
712
+ relays.command('create-zone <name>').description('Create a DNS zone')
713
+ .option('--type <type>', 'NATIVE, MASTER or SLAVE (default NATIVE)').option('--no-default-records', 'skip the default records')
714
+ .action(async (name, o) => {
715
+ const result = await (await client()).relays.zones.create({ name, ...(o.type ? { type: o.type.toUpperCase() } : {}),
716
+ create_default_records: o.defaultRecords });
717
+ emit(result, () => `${c.green('✓')} Created zone ${name}`);
718
+ });
719
+ relays.command('update-zone <zone>').description('Update a zone\'s type or SOA settings')
720
+ .option('--type <type>', 'NATIVE, MASTER or SLAVE').option('--primary-ns <host>').option('--hostmaster <email>')
721
+ .option('--refresh <seconds>').option('--retry <seconds>').option('--expire <seconds>').option('--soa-ttl <seconds>')
722
+ .action(async (zone, o) => {
723
+ const soa = Object.fromEntries(Object.entries({ primary_ns: o.primaryNs, hostmaster: o.hostmaster,
724
+ refresh: dnsInt(o.refresh, '--refresh', 60, 2 ** 31), retry: dnsInt(o.retry, '--retry', 60, 2 ** 31),
725
+ expire: dnsInt(o.expire, '--expire', 3600, 2 ** 31), ttl: dnsInt(o.soaTtl, '--soa-ttl', 60, 2 ** 31) }).filter(([, v]) => v !== undefined));
726
+ const body = { ...(o.type ? { type: o.type.toUpperCase() } : {}), ...(Object.keys(soa).length ? { soa } : {}) };
727
+ if (!Object.keys(body).length)
728
+ throw new Error('Nothing to update: pass --type or an SOA option.');
729
+ const result = await (await client()).relays.zones.update(zone, body);
730
+ emit(result, () => `${c.green('✓')} Updated zone ${zone}`);
731
+ });
732
+ relays.command('delete-zone <zone>').description('Delete a zone and all its records').action(async (zone) => {
733
+ const result = await (await client()).relays.zones.delete(zone);
734
+ emit(result, () => `${c.green('✓')} Deleted zone ${zone}`);
735
+ });
563
736
  const records = relays.command('records').description('DNS records in a zone');
737
+ records.command('get <zone> <record_id>').description('Read one record').action(async (zone, id) => {
738
+ const result = await (await client()).relays.records.get(zone, recordId(id));
739
+ emit(result, () => JSON.stringify(result, null, 2));
740
+ });
741
+ records.command('update <zone> <record_id>').description('Update a record')
742
+ .option('--name <name>').option('--type <type>').option('--content <content>').option('--ttl <seconds>', 'TTL, 60 to 86400')
743
+ .option('--priority <n>', '0 to 65535').option('--comment <text>').option('--disable', 'disable the record').option('--enable', 'enable the record')
744
+ .action(async (zone, id, o) => {
745
+ if (o.disable && o.enable)
746
+ throw new Error('Pass --disable or --enable, not both.');
747
+ const body = Object.fromEntries(Object.entries({ name: o.name, type: o.type?.toUpperCase(), content: o.content,
748
+ ttl: dnsInt(o.ttl, '--ttl', 60, 86400), priority: dnsInt(o.priority, '--priority', 0, 65535), comment: o.comment,
749
+ disabled: o.disable ? true : o.enable ? false : undefined }).filter(([, v]) => v !== undefined));
750
+ if (!Object.keys(body).length)
751
+ throw new Error('Nothing to update: pass at least one field option.');
752
+ const result = await (await client()).relays.records.update(zone, recordId(id), body);
753
+ emit(result, () => `${c.green('✓')} Updated record ${id} in ${zone}`);
754
+ });
564
755
  records.command('list <zone>', { isDefault: true }).option('--type <type>').action(async (zone, o) => {
565
756
  const r = await (await client()).relays.records.list(zone, o.type ? { type: o.type } : {});
566
757
  emit(r, () => table((r.records ?? []), ['id', 'name', 'type', 'content', 'ttl']));
@@ -573,10 +764,31 @@ export function buildProgram(deps = {}) {
573
764
  await (await client()).relays.records.delete(zone, Number(id));
574
765
  emit({ deleted: Number(id) }, () => `${c.green('✓')} Deleted record ${id} from ${zone}`);
575
766
  });
576
- addHenryCommands(program, client, value => out.log(JSON.stringify(value, null, 2)));
767
+ addHenryCommands(program, client, value => out.log(JSON.stringify(value, null, 2)), { line: text => out.log(text), ...(deps.signal ? { signal: deps.signal } : {}) });
577
768
  registerFleets(program, client, (data) => emit(data, () => JSON.stringify(data, null, 2)));
578
- registerFlocks(program, client, (data) => emit(data, () => JSON.stringify(data, null, 2)), env);
769
+ registerFlocks(program, client, emit, env, deps.readFlocksCode, () => !!g().json);
579
770
  registerBeacons(program, client, emit);
771
+ // ---- setup -----------------------------------------------------------
772
+ // The first-run experience lives in ./onboard and is imported only here, so no other command loads it.
773
+ program.command('setup [step]').description('Set up Genesis: sign in, pick a project, add the MCP server to your agents (resumable)')
774
+ .option('--restart', 'start again from the first step')
775
+ .option('--no-animation', 'draw every step still (also GENESIS_REDUCED_MOTION=1)')
776
+ .option('--screen-reader', 'linear output for screen readers (also GENESIS_SCREEN_READER=1)')
777
+ .option('--screencast', 'mask account names, emails and IDs, for recording demos (also GENESIS_SCREENCAST=1)')
778
+ .option('--sound', 'a soft chime when you are signed in and when setup finishes (also GENESIS_SOUND=1)')
779
+ .action(async (step, o) => {
780
+ const code = await runSetupLazily(o, step);
781
+ if (code)
782
+ throw new CommanderError(code, 'genesis.setup', '');
783
+ });
784
+ const runSetupLazily = async (o, step) => {
785
+ const [{ runSetup }, authModule, deviceModule] = await Promise.all([import('./onboard/index.js'), import('./auth.js'), import('./device-login.js')]);
786
+ return runSetup({ version: program.version() ?? '', env, fetch: f, auth, getStore, client: () => client(), openBrowser: deps.openBrowser ?? defaultOpen,
787
+ baseUrl: g().baseUrl ?? env.GENESIS_BASE_URL ?? DEFAULT_BASE_URL, log: (s) => out.log(s),
788
+ flags: { json: g().json, animation: o.animation, screenReader: o.screenReader, sound: o.sound, screencast: o.screencast, restart: o.restart, step },
789
+ login: { startLogin: authModule.startLogin, completeLogin: authModule.completeLogin, selectSavedContext: authModule.selectSavedContext,
790
+ savedLoginProvider: authModule.savedLoginProvider, startDeviceLogin: deviceModule.startDeviceLogin, completeDeviceLogin: deviceModule.completeDeviceLogin } });
791
+ };
580
792
  // ---- status ----------------------------------------------------------
581
793
  program.command('status').description('Live state of each Genesis service, from the gateway (GET /v1/status)').action(async () => {
582
794
  const configured = g().baseUrl ?? env.GENESIS_BASE_URL ?? DEFAULT_BASE_URL;
@@ -591,7 +803,7 @@ export function buildProgram(deps = {}) {
591
803
  const url = `${base.href.replace(/\/+$/, '')}/v1/status`;
592
804
  let response;
593
805
  try {
594
- response = await f(url, { method: 'GET', headers: { accept: 'application/json', 'user-agent': 'genesis-cli/0.1.13' }, signal: AbortSignal.timeout(15_000) });
806
+ response = await f(url, { method: 'GET', headers: { accept: 'application/json', 'user-agent': 'genesis-cli/0.2.0' }, signal: AbortSignal.timeout(15_000) });
595
807
  }
596
808
  catch (e) {
597
809
  throw new Error(`Gateway unreachable at ${safe(url)}: ${safe(e.message)}`);
@@ -609,9 +821,26 @@ export function buildProgram(deps = {}) {
609
821
  });
610
822
  return program;
611
823
  }
824
+ /**
825
+ * A bare `genesis` on a terminal, before setup is finished, is the first run: it opens `genesis setup`. Everywhere
826
+ * else (a pipe, CI, TERM=dumb, setup complete) it prints the help as before. Only the setup state file is read here.
827
+ */
828
+ async function firstRun(argv, deps) {
829
+ if (argv.length > 2 || deps.out)
830
+ return false;
831
+ const env = deps.env ?? process.env;
832
+ const set = (v) => !!v && v !== '0' && v !== 'false';
833
+ if (!process.stdout.isTTY || !process.stdin.isTTY || env.TERM === 'dumb'
834
+ || set(env.CI) || env.GITHUB_ACTIONS || env.BUILDKITE || env.GITLAB_CI || env.TF_BUILD)
835
+ return false;
836
+ const { SetupStore, complete } = await import('./onboard/state.js');
837
+ return !complete(await SetupStore.for(env).read());
838
+ }
612
839
  export async function main(argv, deps = {}) {
613
840
  const out = deps.out ?? stdio;
614
841
  const program = buildProgram(deps);
842
+ if (await firstRun(argv, deps).catch(() => false))
843
+ argv = [...argv, 'setup'];
615
844
  try {
616
845
  await program.parseAsync(argv);
617
846
  return 0;
@@ -621,6 +850,9 @@ export async function main(argv, deps = {}) {
621
850
  return e.exitCode;
622
851
  const json = program.opts().json;
623
852
  if (e instanceof GenesisError) {
853
+ // Dials needs a project, workspace and environment: from flags, a selected context or an org key's binding.
854
+ const dialsScopeHint = e.operationId?.startsWith('dials_') && /invalid_scope/i.test(`${e.code ?? ''} ${e.message} ${e.type}`)
855
+ ? ' Pass --workspace, --project and --environment, select a context (genesis context select), or use an org API key bound to a project.' : '';
624
856
  if (json)
625
857
  out.err(JSON.stringify({ error: { type: e.type, status: e.status, message: e.message, request_id: e.requestId, ...errorCatalog(e),
626
858
  ...((e instanceof HivesOperationError || e instanceof HivesQueueOperationError) ? { operation: e.operation } : {}),
@@ -628,7 +860,7 @@ export async function main(argv, deps = {}) {
628
860
  ...(e instanceof MindsAgentError || e instanceof MindsOwnerError ? { code: e.code, ...(e instanceof MindsAgentError ? { dpop_nonce: e.dpopNonce } : {}),
629
861
  ...(errorOutcome(e.body) !== undefined ? { outcome: errorOutcome(e.body) } : {}) } : {}) } }));
630
862
  else
631
- out.err(`${c.red('✗')} ${safe(e.message)}${(e instanceof HivesOperationError || e instanceof HivesQueueOperationError) ? ` Operation: ${safe(e.operation.operation_id)} (${safe(e.operation.state)}).` : ''}${e.status ? c.dim(` (${e.status} ${safe(e.type)}${e.requestId ? `, request ${safe(e.requestId)}` : ''})`) : ''}`);
863
+ out.err(`${c.red('✗')} ${safe(e.message)}${dialsScopeHint}${(e instanceof HivesOperationError || e instanceof HivesQueueOperationError) ? ` Operation: ${safe(e.operation.operation_id)} (${safe(e.operation.state)}).` : ''}${e.status ? c.dim(` (${e.status} ${safe(e.type)})`) : ''}${e.requestId ? `\n${c.dim(`request_id: ${safe(e.requestId)}`)}` : ''}`);
632
864
  return 1;
633
865
  }
634
866
  const msg = e.message ?? String(e);
package/dist/minds.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { type Command } from 'commander';
2
- import type { Genesis } from '@onesi/genesis';
2
+ import { type Genesis } from '@onesi/genesis';
3
3
  export declare function registerMinds(program: Command, client: () => Promise<Genesis>, emit: (data: unknown) => void, agentClient?: () => Promise<Genesis>, signal?: AbortSignal, ownerClient?: () => Promise<Genesis>): void;
package/dist/minds.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { readFile, stat } from 'node:fs/promises';
2
2
  import { registerMindsOwner } from './minds-owner.js';
3
3
  import { Option } from 'commander';
4
+ import { mindsServices } from '@onesi/genesis';
4
5
  export function registerMinds(program, client, emit, agentClient = client, signal, ownerClient = client) {
5
- const minds = program.command('minds').description('Owned Minds and persisted memory; requires an admitted human Console Sandbox session');
6
+ // `minds engine ...` (generated, products.ts) may already have created the root.
7
+ const minds = (program.commands.find(c => c.name() === 'minds') ?? program.command('minds')).description('Owned Minds and persisted memory; a human Console Sandbox session, or an org API key with minds:read/minds:write/minds:manage');
6
8
  registerMindsOwner(minds, ownerClient, emit, signal);
7
9
  minds.command('list').option('--after <uuid>').option('--limit <number>').description('List real Minds and the current creation context fingerprint').action(async (options) => {
8
10
  emit(await (await client()).minds.list({ ...(options.after ? { after: options.after } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) }));
@@ -17,7 +19,13 @@ export function registerMinds(program, client, emit, agentClient = client, signa
17
19
  .requiredOption('--context-sha256 <hash>', 'context_sha256 from the current minds list response')
18
20
  .addOption(new Option('--plan <plan>', 'explicit plan; entitlement remains server-authorized').choices(['free', 'pro']))
19
21
  .addOption(new Option('--profile-id <profile>', 'fixed native profile; Pro defaults to D1, Free accepts only F1').choices(['F1', 'D1', 'D2']))
20
- .action(async (name, options) => emit(await (await client()).minds.create(name, { idempotencyKey: options.idempotencyKey, contextSha256: options.contextSha256, ...(options.plan === undefined ? {} : { plan: options.plan }), ...(options.profileId === undefined ? {} : { profileId: options.profileId }) })));
22
+ .option('--services <list>', `comma-separated engine service groups (${mindsServices.join(', ')}); kv and memory always run`)
23
+ .action(async (name, options) => {
24
+ const services = options.services === undefined ? undefined : String(options.services).split(',').map((item) => item.trim()).filter(Boolean);
25
+ if (services && services.some(item => !mindsServices.includes(item)))
26
+ throw new Error(`Unknown Minds service; choose from ${mindsServices.join(', ')}.`);
27
+ emit(await (await client()).minds.create(name, { idempotencyKey: options.idempotencyKey, contextSha256: options.contextSha256, ...(options.plan === undefined ? {} : { plan: options.plan }), ...(options.profileId === undefined ? {} : { profileId: options.profileId }), ...(services === undefined ? {} : { services }) }));
28
+ });
21
29
  minds.command('resume <mind_id>').description('Resume the same Mind and retained volume; 202 means the operation is accepted')
22
30
  .requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
23
31
  .requiredOption('--idempotency-key <uuid>', 'persist this UUID4 with the original generation after an uncertain response')
@@ -27,6 +35,23 @@ export function registerMinds(program, client, emit, agentClient = client, signa
27
35
  .requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
28
36
  .requiredOption('--idempotency-key <uuid>', 'persist this UUID4 and original generation; no automatic retry')
29
37
  .action(async (id, options) => emit(await (await client()).minds.stop(id, Number(options.expectedGeneration), { idempotencyKey: options.idempotencyKey })));
38
+ minds.command('delete <mind_id>').description('Delete the Mind (lifecycle delete; org API keys need minds:write); 202 means accepted')
39
+ .requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
40
+ .requiredOption('--idempotency-key <uuid>', 'persist this UUID4 and original generation; no automatic retry')
41
+ .action(async (id, options) => emit(await (await client()).minds.delete(id, Number(options.expectedGeneration), { idempotencyKey: options.idempotencyKey })));
42
+ minds.command('resize <mind_id>').description('Resize to an existing Pro profile (org API keys need minds:manage); 202 means accepted')
43
+ .requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
44
+ .addOption(new Option('--profile-id <profile>', 'existing Pro profile').choices(['F1', 'D1', 'D2']).makeOptionMandatory())
45
+ .requiredOption('--idempotency-key <uuid>', 'persist this UUID4 and original generation; no automatic retry')
46
+ .action(async (id, options) => emit(await (await client()).minds.resize(id, Number(options.expectedGeneration), options.profileId, { idempotencyKey: options.idempotencyKey })));
47
+ const deliveries = minds.command('webhook-deliveries').description('Durable webhook receipts for an owned Mind; a stopped-idle Mind is woken');
48
+ deliveries.command('create <mind_id>').description('Record one delivery; replay the same delivery_id and body to read the original receipt')
49
+ .requiredOption('--delivery-id <uuid>', 'caller-chosen non-nil UUID; retain it after an uncertain response')
50
+ .requiredOption('--event-reference <reference>', 'opaque upstream event reference ([A-Za-z0-9_.:/-], at most 200)')
51
+ .requiredOption('--event-sha256 <hash>', 'lowercase hex SHA-256 of the upstream event')
52
+ .action(async (id, options) => emit(await (await client()).minds.webhookDeliveries.create(id, { delivery_id: options.deliveryId, event_reference: options.eventReference, event_sha256: options.eventSha256 })));
53
+ deliveries.command('get <mind_id> <delivery_id>').description('Read a receipt this principal issued')
54
+ .action(async (id, delivery) => emit(await (await client()).minds.webhookDeliveries.get(id, delivery)));
30
55
  const memory = minds.command('memory').description('Every operation selects one owned, ready Mind');
31
56
  memory.command('insert <mind_id>').requiredOption('--event-file <path>', 'JSON event with id, content and timestamp; returned id is its storage UUID')
32
57
  .action(async (id, options) => {
@@ -55,6 +80,16 @@ export function registerMinds(program, client, emit, agentClient = client, signa
55
80
  emit(await (await client()).minds.memory.search(mind, text, Number(options.limit)));
56
81
  });
57
82
  const agent = minds.command('agent').description('Use an existing agent credential; does not create an attachment or exchange a human session');
83
+ const createProof = async (path) => ({ signal, dpopProof: await readBounded(path, 4_096) });
84
+ agent.command('create <name>').description('Create a Free/F1 Sandbox Mind with an approved agent-create token; 202 means provisioning')
85
+ .requiredOption('--approval-id <uuid>', 'owner approval admitting this creation')
86
+ .requiredOption('--approval-version <number>', 'approval version')
87
+ .requiredOption('--operation-id <uuid>', 'persisted UUID4; also the idempotency key and the status lookup id')
88
+ .requiredOption('--dpop-proof-file <path>', 'fresh DPoP proof for the controller create method and URL')
89
+ .action(async (name, options) => emit(await (await agentClient()).minds.agent.create({ name, approvalId: options.approvalId, approvalVersion: Number(options.approvalVersion), operationId: options.operationId }, await createProof(options.dpopProofFile))));
90
+ agent.command('create-status <operation_id>').description('Read an agent Mind creation operation')
91
+ .requiredOption('--dpop-proof-file <path>', 'fresh DPoP proof for the controller status method and URL')
92
+ .action(async (operation, options) => emit(await (await agentClient()).minds.agent.createStatus(operation, await createProof(options.dpopProofFile))));
58
93
  const agentMemory = agent.command('memory').description('Private or explicitly authorized group memory; requests are never automatically retried');
59
94
  const scoped = (command) => command.option('--group <agp_id>', 'explicit canonical group; omit for private memory')
60
95
  .option('--dpop-proof-file <path>', 'fresh DPoP proof for the controller capability method and URL');
@@ -0,0 +1,50 @@
1
+ /**
2
+ * One's marks as drawings: the emblem (Digital One, the disc with the eye knocked out), the Ones, and the hybrid ONE
3
+ * logotype whose O can close to the disc, wake as the eye, hop through the Ones and open back to the ring (JR-B103's
4
+ * cycle, the One site's components/ui/nav-logotype.tsx). Everything is drawn into a raster.Canvas from brand.ts.
5
+ *
6
+ * Paint rules (one-system.css): the disc is One's black on light and chalk on dark, never outlined or glowing; amber
7
+ * is only ever light inside the eye; the Ones wear their own colours, and those colours mean nothing else.
8
+ */
9
+ import { ONES, type OneShape } from './brand.js';
10
+ import { Canvas, type Color, type Mat } from './raster.js';
11
+ import { sample, LIGHT, type Pose } from './eye.js';
12
+ /**
13
+ * The terminal's own foreground. When the probe could not learn the background we draw ink and Digital One in this
14
+ * colour and the encoder emits SGR 39, so the mark is always the user's own ink: black on light, chalk on dark.
15
+ */
16
+ export declare const DEFAULT_INK: Color;
17
+ export declare const isDefaultInk: (c: Color) => boolean;
18
+ export type Tone = 'light' | 'dark' | 'unknown';
19
+ export declare const inkFor: (tone: Tone) => Color;
20
+ export interface Head {
21
+ /** null: Digital One. Otherwise one of the Ones. */
22
+ one: OneShape | null;
23
+ pose: Pose;
24
+ /** agent-eye's light moment, -1 (waiting below) … 0 (full) … 1 (gone above); amber, inside the eye only. */
25
+ light?: number;
26
+ /** Squash for a hop: 1 is at rest; below 1 squats onto the pivot, above stretches. */
27
+ squash?: number;
28
+ /** Vertical offset in head units (a hop's lift). */
29
+ hop?: number;
30
+ }
31
+ /** Draw a head into the -100…100 box mapped by `m`. */
32
+ export declare function drawHead(cv: Canvas, m: Mat, head: Head, tone: Tone): void;
33
+ export declare const LOGO_ASPECT: number;
34
+ export interface LogoState {
35
+ /** 0…1: the ring draws on clockwise from twelve o'clock. */
36
+ ring: number;
37
+ /** 0 the ring … 1 the disc (its hole closes on the enter curve). */
38
+ fill: number;
39
+ /** When set, the O is a head (One's eye awake, or one of the Ones) in the O's circle. */
40
+ head?: Head | null;
41
+ /** 0…1 each: the N and the E arrive, rising into place. */
42
+ n: number;
43
+ e: number;
44
+ }
45
+ export declare const LOGO_REST: LogoState;
46
+ /** The hybrid ONE, `px` tall (cap height plus the O's overshoot), its top-left at (x, y) in canvas pixels. */
47
+ export declare function drawLogotype(cv: Canvas, x: number, y: number, px: number, s: LogoState, tone: Tone, hint?: boolean): void;
48
+ /** Digital One, `px` across, centred at (cx, cy). */
49
+ export declare function drawEmblem(cv: Canvas, cx: number, cy: number, px: number, head: Partial<Head>, tone: Tone): void;
50
+ export { ONES, LIGHT, sample };