@onesi/genesis-cli 0.0.1-stage → 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.
- package/README.md +64 -0
- package/bin/genesis.js +3 -0
- package/dist/auth.d.ts +30 -0
- package/dist/auth.js +273 -0
- package/dist/beacons.d.ts +3 -0
- package/dist/beacons.js +162 -0
- package/dist/device-login.d.ts +25 -0
- package/dist/device-login.js +173 -0
- package/dist/dials.d.ts +7 -0
- package/dist/dials.js +99 -0
- package/dist/fleets.d.ts +4 -0
- package/dist/fleets.js +102 -0
- package/dist/flocks.d.ts +9 -0
- package/dist/flocks.js +326 -0
- package/dist/function-input.d.ts +3 -0
- package/dist/function-input.js +49 -0
- package/dist/henry.d.ts +7 -0
- package/dist/henry.js +85 -0
- package/dist/hives-coverage.d.ts +12 -0
- package/dist/hives-coverage.js +116 -0
- package/dist/hives-marketplace.d.ts +10 -0
- package/dist/hives-marketplace.js +29 -0
- package/dist/hives-node-transport.d.ts +6 -0
- package/dist/hives-node-transport.js +150 -0
- package/dist/hives-operations.d.ts +10 -0
- package/dist/hives-operations.js +63 -0
- package/dist/hives-platform.d.ts +13 -0
- package/dist/hives-platform.js +133 -0
- package/dist/hives-resources.d.ts +9 -0
- package/dist/hives-resources.js +94 -0
- package/dist/iam-directory.d.ts +15 -0
- package/dist/iam-directory.js +71 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +872 -0
- package/dist/lifeid-native.d.ts +24 -0
- package/dist/lifeid-native.js +113 -0
- package/dist/minds-owner.d.ts +3 -0
- package/dist/minds-owner.js +42 -0
- package/dist/minds.d.ts +3 -0
- package/dist/minds.js +125 -0
- package/dist/onboard/art.d.ts +50 -0
- package/dist/onboard/art.js +139 -0
- package/dist/onboard/brand.d.ts +56 -0
- package/dist/onboard/brand.js +134 -0
- package/dist/onboard/caps.d.ts +90 -0
- package/dist/onboard/caps.js +232 -0
- package/dist/onboard/encode.d.ts +23 -0
- package/dist/onboard/encode.js +235 -0
- package/dist/onboard/eye.d.ts +103 -0
- package/dist/onboard/eye.js +147 -0
- package/dist/onboard/index.d.ts +61 -0
- package/dist/onboard/index.js +562 -0
- package/dist/onboard/mcp.d.ts +35 -0
- package/dist/onboard/mcp.js +118 -0
- package/dist/onboard/raster.d.ts +43 -0
- package/dist/onboard/raster.js +253 -0
- package/dist/onboard/sound.d.ts +2 -0
- package/dist/onboard/sound.js +54 -0
- package/dist/onboard/state.d.ts +24 -0
- package/dist/onboard/state.js +48 -0
- package/dist/onboard/term.d.ts +69 -0
- package/dist/onboard/term.js +292 -0
- package/dist/onboard/ui.d.ts +97 -0
- package/dist/onboard/ui.js +615 -0
- package/dist/orgs.d.ts +16 -0
- package/dist/orgs.js +89 -0
- package/dist/output.d.ts +28 -0
- package/dist/output.js +46 -0
- package/dist/products.d.ts +18 -0
- package/dist/products.js +311 -0
- package/dist/relays-commands/certificates.d.ts +3 -0
- package/dist/relays-commands/certificates.js +20 -0
- package/dist/relays-commands/domains.d.ts +3 -0
- package/dist/relays-commands/domains.js +23 -0
- package/dist/relays-commands/index.d.ts +4 -0
- package/dist/relays-commands/index.js +10 -0
- package/dist/relays-commands/monitoring.d.ts +3 -0
- package/dist/relays-commands/monitoring.js +41 -0
- package/dist/relays-commands/telemetry.d.ts +3 -0
- package/dist/relays-commands/telemetry.js +28 -0
- package/dist/relays-commands/zones.d.ts +5 -0
- package/dist/relays-commands/zones.js +9 -0
- package/dist/relays-enrollment.d.ts +3 -0
- package/dist/relays-enrollment.js +7 -0
- package/dist/runner.d.ts +4 -0
- package/dist/runner.js +57 -0
- package/dist/secret-input.d.ts +14 -0
- package/dist/secret-input.js +85 -0
- package/dist/store.d.ts +39 -0
- package/dist/store.js +33 -0
- package/dist/wasm-input.d.ts +2 -0
- package/dist/wasm-input.js +31 -0
- package/package.json +54 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const LIFEID_ISSUER = "https://auth.life.one";
|
|
2
|
+
export declare const GENESIS_CLI_PRODUCT = "genesis-cli";
|
|
3
|
+
export declare const CALLBACK_PATH = "/lifeid/callback";
|
|
4
|
+
/** Exact Auth-owned admission metadata; supplying it does not register a client. */
|
|
5
|
+
export interface NativeRegistration {
|
|
6
|
+
productId: string;
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
audience: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NativeAttempt {
|
|
11
|
+
readonly browserUrl: string;
|
|
12
|
+
readonly codeVerifier: string;
|
|
13
|
+
readonly registration: NativeRegistration;
|
|
14
|
+
readonly code: Promise<string>;
|
|
15
|
+
readonly signal: AbortSignal;
|
|
16
|
+
close(): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function validateRegistration(r: NativeRegistration): number;
|
|
19
|
+
/** Bind the sole registered port before the caller opens or prints the browser URL. */
|
|
20
|
+
export declare function startNativeAttempt(registration: NativeRegistration, options?: {
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
organizationHint?: string;
|
|
24
|
+
}): Promise<NativeAttempt>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
export const LIFEID_ISSUER = 'https://auth.life.one';
|
|
4
|
+
export const GENESIS_CLI_PRODUCT = 'genesis-cli';
|
|
5
|
+
export const CALLBACK_PATH = '/lifeid/callback';
|
|
6
|
+
export function validateRegistration(r) {
|
|
7
|
+
const m = /^http:\/\/127\.0\.0\.1:([1-9][0-9]{3,4})\/lifeid\/callback$/.exec(r.redirectUri);
|
|
8
|
+
const port = m ? Number(m[1]) : 0;
|
|
9
|
+
if (r.productId !== GENESIS_CLI_PRODUCT || !m || port < 1024 || port > 65535
|
|
10
|
+
|| !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(r.audience)) {
|
|
11
|
+
throw new Error('One account native login requires the exact admitted Genesis product, loopback callback and audience.');
|
|
12
|
+
}
|
|
13
|
+
return port;
|
|
14
|
+
}
|
|
15
|
+
/** Bind the sole registered port before the caller opens or prints the browser URL. */
|
|
16
|
+
export async function startNativeAttempt(registration, options = {}) {
|
|
17
|
+
registration = { ...registration };
|
|
18
|
+
const port = validateRegistration(registration);
|
|
19
|
+
const timeout = options.timeoutMs ?? 300_000;
|
|
20
|
+
if (!Number.isInteger(timeout) || timeout < 1 || timeout > 300_000)
|
|
21
|
+
throw new Error('Invalid login deadline.');
|
|
22
|
+
if (options.signal?.aborted)
|
|
23
|
+
throw new Error('One account login cancelled.');
|
|
24
|
+
if (options.organizationHint && !/^o_[0-9A-HJKMNP-TV-Z]{26}$/.test(options.organizationHint)) {
|
|
25
|
+
throw new Error('Invalid One account organization hint.');
|
|
26
|
+
}
|
|
27
|
+
const verifier = randomBytes(48).toString('base64url');
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
let settled = false;
|
|
30
|
+
let resolveCode;
|
|
31
|
+
let rejectCode;
|
|
32
|
+
const code = new Promise((resolve, reject) => { resolveCode = resolve; rejectCode = reject; });
|
|
33
|
+
// Binding/open-browser errors may happen before the caller awaits the code.
|
|
34
|
+
void code.catch(() => { });
|
|
35
|
+
const server = createServer({ maxHeaderSize: 4096 }, (req, res) => {
|
|
36
|
+
const reject = (status) => {
|
|
37
|
+
res.writeHead(status, { 'content-type': 'text/plain', 'cache-control': 'no-store', connection: 'close' });
|
|
38
|
+
res.end('Invalid One account callback.');
|
|
39
|
+
};
|
|
40
|
+
// A malformed request cannot consume a pending code. No body is read/logged.
|
|
41
|
+
if (settled || req.method !== 'GET' || req.headers.host !== `127.0.0.1:${port}`
|
|
42
|
+
|| req.headers.origin !== undefined || req.headers['transfer-encoding'] !== undefined
|
|
43
|
+
|| (req.headers['content-length'] !== undefined && req.headers['content-length'] !== '0')) {
|
|
44
|
+
reject(400);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const m = /^\/lifeid\/callback\?life_code=([A-Za-z0-9_-]{43})$/.exec(req.url ?? '');
|
|
48
|
+
if (!m) {
|
|
49
|
+
reject(400);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
settled = true;
|
|
53
|
+
res.writeHead(200, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store',
|
|
54
|
+
'referrer-policy': 'no-referrer', 'content-security-policy': "default-src 'none'", connection: 'close' });
|
|
55
|
+
res.end('One account callback received. Return to Genesis CLI.');
|
|
56
|
+
server.close();
|
|
57
|
+
resolveCode(m[1]);
|
|
58
|
+
});
|
|
59
|
+
server.requestTimeout = 5000;
|
|
60
|
+
server.headersTimeout = 5000;
|
|
61
|
+
server.keepAliveTimeout = 1;
|
|
62
|
+
server.maxConnections = 4;
|
|
63
|
+
const close = () => {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
66
|
+
server.close();
|
|
67
|
+
server.closeAllConnections();
|
|
68
|
+
if (!settled) {
|
|
69
|
+
settled = true;
|
|
70
|
+
rejectCode(new Error('One account login cancelled.'));
|
|
71
|
+
}
|
|
72
|
+
controller.abort();
|
|
73
|
+
};
|
|
74
|
+
const onAbort = () => close();
|
|
75
|
+
const timer = setTimeout(() => {
|
|
76
|
+
if (!settled) {
|
|
77
|
+
settled = true;
|
|
78
|
+
rejectCode(new Error('One account login timed out. Run `genesis login` again.'));
|
|
79
|
+
}
|
|
80
|
+
close();
|
|
81
|
+
}, timeout);
|
|
82
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
83
|
+
try {
|
|
84
|
+
await new Promise((resolve, reject) => {
|
|
85
|
+
const aborted = () => { cleanup(); reject(new Error('One account login cancelled.')); };
|
|
86
|
+
const cleanup = () => { server.removeListener('error', error); controller.signal.removeEventListener('abort', aborted); };
|
|
87
|
+
const error = () => { cleanup(); reject(new Error('The registered One account callback port is unavailable. No other port was tried.')); };
|
|
88
|
+
server.once('error', error);
|
|
89
|
+
controller.signal.addEventListener('abort', aborted, { once: true });
|
|
90
|
+
if (controller.signal.aborted) {
|
|
91
|
+
aborted();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
server.listen({ port, host: '127.0.0.1', signal: controller.signal }, () => { cleanup(); resolve(); });
|
|
95
|
+
});
|
|
96
|
+
if (controller.signal.aborted || options.signal?.aborted)
|
|
97
|
+
throw new Error('One account login cancelled.');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
close();
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
server.on('error', () => close());
|
|
104
|
+
const url = new URL('/v4/lifeid/start', LIFEID_ISSUER);
|
|
105
|
+
url.searchParams.set('product_id', registration.productId);
|
|
106
|
+
url.searchParams.set('return_path', '/');
|
|
107
|
+
url.searchParams.set('code_challenge', createHash('sha256').update(verifier).digest('base64url'));
|
|
108
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
109
|
+
if (options.organizationHint)
|
|
110
|
+
url.searchParams.set('org_hint', options.organizationHint);
|
|
111
|
+
return { browserUrl: url.toString(), codeVerifier: verifier, registration: { ...registration }, code,
|
|
112
|
+
signal: controller.signal, close };
|
|
113
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
export function registerMindsOwner(minds, client, emit, signal) {
|
|
3
|
+
const intent = (command, revision = true) => {
|
|
4
|
+
command.requiredOption('--operation-id <uuid>', 'retain this non-nil UUID when reconciling the same intent');
|
|
5
|
+
if (revision)
|
|
6
|
+
command.requiredOption('--expected-version <integer>', 'current membership or attachment version; zero for first creation');
|
|
7
|
+
return command;
|
|
8
|
+
};
|
|
9
|
+
const version = (value) => {
|
|
10
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)))
|
|
11
|
+
throw new Error('Expected version must be a nonnegative safe integer.');
|
|
12
|
+
return Number(value);
|
|
13
|
+
};
|
|
14
|
+
const body = (options) => ({ operationId: options.operationId, expectedVersion: version(options.expectedVersion) });
|
|
15
|
+
const groups = minds.command('groups').description('Canonical owner-managed groups; uses the original selected Console human session');
|
|
16
|
+
groups.command('list').action(async () => emit(await (await client()).minds.groups.list({ signal })));
|
|
17
|
+
intent(groups.command('create <name>'), false).action(async (name, options) => emit(await (await client()).minds.groups.create({ operationId: options.operationId, name }, { signal })));
|
|
18
|
+
const members = groups.command('members').description('Canonical group memberships and retained removal versions');
|
|
19
|
+
members.command('list <group_id>').action(async (group) => emit(await (await client()).minds.groups.members.list(group, { signal })));
|
|
20
|
+
intent(members.command('add <group_id> <agent_id>')).action(async (group, agent, options) => emit(await (await client()).minds.groups.members.add(group, { ...body(options), agentId: agent }, { signal })));
|
|
21
|
+
intent(members.command('remove <group_id> <agent_id>')).action(async (group, agent, options) => emit(await (await client()).minds.groups.members.remove(group, agent, body(options), { signal })));
|
|
22
|
+
const attachments = minds.command('attachments').description('Attach existing canonical agents or groups; never issues credentials');
|
|
23
|
+
attachments.command('list <mind_id>').action(async (mind) => emit(await (await client()).minds.attachments.list(mind, { signal })));
|
|
24
|
+
attachments.command('attach <mind_id>').requiredOption('--request-file <path>', 'closed JSON attachment intent with operationId, expectedVersion, subject, permissions and expiresAt')
|
|
25
|
+
.action(async (mind, options) => {
|
|
26
|
+
const metadata = await stat(options.requestFile);
|
|
27
|
+
if (!metadata.isFile() || metadata.size > 8192)
|
|
28
|
+
throw new Error('Attachment request must be at most 8 KiB.');
|
|
29
|
+
const bytes = await readFile(options.requestFile);
|
|
30
|
+
if (bytes.length > 8192)
|
|
31
|
+
throw new Error('Attachment request must be at most 8 KiB.');
|
|
32
|
+
let request;
|
|
33
|
+
try {
|
|
34
|
+
request = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error('Attachment request must contain valid JSON.');
|
|
38
|
+
}
|
|
39
|
+
emit(await (await client()).minds.attachments.attach(mind, request, { signal }));
|
|
40
|
+
});
|
|
41
|
+
intent(attachments.command('detach <mind_id> <attachment_id>')).action(async (mind, attachment, options) => emit(await (await client()).minds.attachments.detach(mind, attachment, body(options), { signal })));
|
|
42
|
+
}
|
package/dist/minds.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type Command } from 'commander';
|
|
2
|
+
import { type Genesis } from '@onesi/genesis';
|
|
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
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { registerMindsOwner } from './minds-owner.js';
|
|
3
|
+
import { Option } from 'commander';
|
|
4
|
+
import { mindsServices } from '@onesi/genesis';
|
|
5
|
+
export function registerMinds(program, client, emit, agentClient = client, signal, ownerClient = client) {
|
|
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');
|
|
8
|
+
registerMindsOwner(minds, ownerClient, emit, signal);
|
|
9
|
+
minds.command('list').option('--after <uuid>').option('--limit <number>').description('List real Minds and the current creation context fingerprint').action(async (options) => {
|
|
10
|
+
emit(await (await client()).minds.list({ ...(options.after ? { after: options.after } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) }));
|
|
11
|
+
});
|
|
12
|
+
minds.command('get <mind_id>').action(async (id) => emit(await (await client()).minds.get(id)));
|
|
13
|
+
minds.command('usage').description('Read selected-project native usage for an explicit window of at most 93 days')
|
|
14
|
+
.requiredOption('--from <timestamp>', 'RFC3339 window start')
|
|
15
|
+
.requiredOption('--to <timestamp>', 'RFC3339 window end')
|
|
16
|
+
.action(async (options) => emit(await (await client()).minds.usage({ from: options.from, to: options.to })));
|
|
17
|
+
minds.command('create <name>').description('Create a Sandbox Mind; Free/F1 by default or an admitted Pro profile; 202 means provisioning')
|
|
18
|
+
.requiredOption('--idempotency-key <uuid>', 'persist this UUID4 and retain it after an uncertain response')
|
|
19
|
+
.requiredOption('--context-sha256 <hash>', 'context_sha256 from the current minds list response')
|
|
20
|
+
.addOption(new Option('--plan <plan>', 'explicit plan; entitlement remains server-authorized').choices(['free', 'pro']))
|
|
21
|
+
.addOption(new Option('--profile-id <profile>', 'fixed native profile; Pro defaults to D1, Free accepts only F1').choices(['F1', 'D1', 'D2']))
|
|
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
|
+
});
|
|
29
|
+
minds.command('resume <mind_id>').description('Resume the same Mind and retained volume; 202 means the operation is accepted')
|
|
30
|
+
.requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
|
|
31
|
+
.requiredOption('--idempotency-key <uuid>', 'persist this UUID4 with the original generation after an uncertain response')
|
|
32
|
+
.addOption(new Option('--wake-reason <reason>', 'reason for this resume intent').choices(['manual', 'scheduled_job', 'webhook', 'memory_consolidation', 'activity']))
|
|
33
|
+
.action(async (id, options) => emit(await (await client()).minds.resume(id, Number(options.expectedGeneration), { idempotencyKey: options.idempotencyKey, ...(options.wakeReason === undefined ? {} : { wakeReason: options.wakeReason }) })));
|
|
34
|
+
minds.command('stop <mind_id>').description('Stop the same Mind without deleting its retained volume; 202 means accepted')
|
|
35
|
+
.requiredOption('--expected-generation <number>', 'desired_generation from a fresh owned Mind read')
|
|
36
|
+
.requiredOption('--idempotency-key <uuid>', 'persist this UUID4 and original generation; no automatic retry')
|
|
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)));
|
|
55
|
+
const memory = minds.command('memory').description('Every operation selects one owned, ready Mind');
|
|
56
|
+
memory.command('insert <mind_id>').requiredOption('--event-file <path>', 'JSON event with id, content and timestamp; returned id is its storage UUID')
|
|
57
|
+
.action(async (id, options) => {
|
|
58
|
+
const metadata = await stat(options.eventFile);
|
|
59
|
+
if (!metadata.isFile() || metadata.size > 65_536)
|
|
60
|
+
throw new Error('Event file must be at most 64 KiB.');
|
|
61
|
+
const bytes = await readFile(options.eventFile);
|
|
62
|
+
if (bytes.length > 65_536)
|
|
63
|
+
throw new Error('Event file exceeds 64 KiB.');
|
|
64
|
+
const event = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
65
|
+
emit(await (await client()).minds.memory.insert(id, event));
|
|
66
|
+
});
|
|
67
|
+
memory.command('get <mind_id> <storage_id>').action(async (mind, storage) => emit(await (await client()).minds.memory.get(mind, storage)));
|
|
68
|
+
memory.command('update <mind_id> <storage_id>').requiredOption('--content-file <path>', 'JSON {event_id,content}; preserves stored metadata, never automatically retries')
|
|
69
|
+
.action(async (mind, storage, options) => {
|
|
70
|
+
const metadata = await stat(options.contentFile);
|
|
71
|
+
if (!metadata.isFile() || metadata.size > 65_536)
|
|
72
|
+
throw new Error('Update file must be at most 64 KiB.');
|
|
73
|
+
const bytes = await readFile(options.contentFile);
|
|
74
|
+
if (bytes.length > 65_536)
|
|
75
|
+
throw new Error('Update file exceeds 64 KiB.');
|
|
76
|
+
const body = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
77
|
+
emit(await (await client()).minds.memory.update(mind, storage, body));
|
|
78
|
+
});
|
|
79
|
+
memory.command('search <mind_id> <text>').option('--limit <number>', '1–100 results', '20').action(async (mind, text, options) => {
|
|
80
|
+
emit(await (await client()).minds.memory.search(mind, text, Number(options.limit)));
|
|
81
|
+
});
|
|
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))));
|
|
93
|
+
const agentMemory = agent.command('memory').description('Private or explicitly authorized group memory; requests are never automatically retried');
|
|
94
|
+
const scoped = (command) => command.option('--group <agp_id>', 'explicit canonical group; omit for private memory')
|
|
95
|
+
.option('--dpop-proof-file <path>', 'fresh DPoP proof for the controller capability method and URL');
|
|
96
|
+
const readBounded = async (path, limit) => {
|
|
97
|
+
const metadata = await stat(path);
|
|
98
|
+
if (!metadata.isFile() || metadata.size > limit)
|
|
99
|
+
throw new Error('Agent input file exceeds its byte limit.');
|
|
100
|
+
const bytes = await readFile(path);
|
|
101
|
+
if (bytes.length > limit)
|
|
102
|
+
throw new Error('Agent input file exceeds its byte limit.');
|
|
103
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
104
|
+
};
|
|
105
|
+
const agentOptions = async (options) => ({ signal,
|
|
106
|
+
...(options.group === undefined ? {} : { group: options.group }),
|
|
107
|
+
...(options.dpopProofFile === undefined ? {} : { dpopProof: await readBounded(options.dpopProofFile, 16_384) }),
|
|
108
|
+
});
|
|
109
|
+
scoped(agentMemory.command('insert <mind_id>').requiredOption('--event-file <path>', 'JSON event; retain the returned storage UUID'))
|
|
110
|
+
.action(async (mind, options) => {
|
|
111
|
+
const content = await readBounded(options.eventFile, 65_536);
|
|
112
|
+
let event;
|
|
113
|
+
try {
|
|
114
|
+
event = JSON.parse(content);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
throw new Error('Agent event file must contain valid JSON.');
|
|
118
|
+
}
|
|
119
|
+
emit(await (await agentClient()).minds.agent.memory.insert(mind, event, await agentOptions(options)));
|
|
120
|
+
});
|
|
121
|
+
scoped(agentMemory.command('get <mind_id> <storage_id>'))
|
|
122
|
+
.action(async (mind, storage, options) => emit(await (await agentClient()).minds.agent.memory.get(mind, storage, await agentOptions(options))));
|
|
123
|
+
scoped(agentMemory.command('search <mind_id> <text>').option('--limit <number>', '1–100 results', '20'))
|
|
124
|
+
.action(async (mind, text, options) => emit(await (await agentClient()).minds.agent.memory.search(mind, text, Number(options.limit), await agentOptions(options))));
|
|
125
|
+
}
|
|
@@ -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 };
|
|
@@ -0,0 +1,139 @@
|
|
|
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 { AMBER, DISC, INK, LOGOTYPE, NIGHT_INK, ONES } from './brand.js';
|
|
10
|
+
import { circle, hex, mix, mul, parsePath, roundRect, scale, translate } from './raster.js';
|
|
11
|
+
import { GEOMETRY, poseToBar, poseToMouth, sample, LIGHT, REST } 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 const DEFAULT_INK = { r: 1, g: 2, b: 3 };
|
|
17
|
+
export const isDefaultInk = (c) => c.r === 1 && c.g === 2 && c.b === 3;
|
|
18
|
+
export const inkFor = (tone) => tone === 'unknown' ? DEFAULT_INK : hex(tone === 'dark' ? NIGHT_INK : INK);
|
|
19
|
+
const discFor = (tone) => tone === 'unknown' ? DEFAULT_INK : hex(tone === 'dark' ? DISC.chalk : DISC.black);
|
|
20
|
+
/** Clip a convex polygon to y in [y0, y1] (Sutherland–Hodgman on two half-planes). */
|
|
21
|
+
function clipY(poly, y0, y1) {
|
|
22
|
+
const pass = (p, keep, edge) => {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (let i = 0; i < p.length; i++) {
|
|
25
|
+
const a = p[i], b = p[(i + 1) % p.length], ia = keep(a[1]), ib = keep(b[1]);
|
|
26
|
+
if (ia)
|
|
27
|
+
out.push(a);
|
|
28
|
+
if (ia !== ib) {
|
|
29
|
+
const t = (edge - a[1]) / (b[1] - a[1]);
|
|
30
|
+
out.push([a[0] + (b[0] - a[0]) * t, edge]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
};
|
|
35
|
+
return pass(pass(poly, (y) => y >= y0, y0), (y) => y <= y1, y1);
|
|
36
|
+
}
|
|
37
|
+
/** Draw a head into the -100…100 box mapped by `m`. */
|
|
38
|
+
export function drawHead(cv, m, head, tone) {
|
|
39
|
+
const sq = head.squash ?? 1, lift = head.hop ?? 0;
|
|
40
|
+
const pivot = 100; // the head stands on its foot: squash about y = +100
|
|
41
|
+
const body = mul(m, mul(translate(0, pivot + lift), mul(scale(1 / Math.sqrt(sq), sq), translate(0, -pivot))));
|
|
42
|
+
const one = head.one;
|
|
43
|
+
const eyeM = one ? mul(body, mul(translate(one.eye.x, one.eye.y), scale(one.eye.scale))) : body;
|
|
44
|
+
const pose = one ? { ...head.pose, open: head.pose.open * one.eye.openScale, look: head.pose.look * one.eye.lookGain } : head.pose;
|
|
45
|
+
const bar = poseToBar(pose);
|
|
46
|
+
const r = Math.min(GEOMETRY.cornerRadius, bar.h / 2, bar.w / 2);
|
|
47
|
+
const barPoly = roundRect(bar.x, bar.y, bar.w, bar.h, r);
|
|
48
|
+
const mouth = poseToMouth(pose);
|
|
49
|
+
const mouthPoly = mouth ? roundRect(mouth.x, mouth.y, mouth.w, mouth.h, GEOMETRY.cornerRadius) : null;
|
|
50
|
+
const toBody = (p) => p.map(([x, y]) => [eyeM[0] * x + eyeM[2] * y + eyeM[4], eyeM[1] * x + eyeM[3] * y + eyeM[5]]);
|
|
51
|
+
if (!one) {
|
|
52
|
+
// Digital One: the disc with the eye (and smile) knocked out, as the still emblem's even-odd cut.
|
|
53
|
+
const disc = circle(0, 0, 100).map(([x, y]) => [body[0] * x + body[2] * y + body[4], body[1] * x + body[3] * y + body[5]]);
|
|
54
|
+
cv.fill([disc, toBody(barPoly), ...(mouthPoly ? [toBody(mouthPoly)] : [])], discFor(tone), undefined, 'evenodd');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
cv.fill(parsePath(one.d), hex(one.colour), body);
|
|
58
|
+
cv.fill([toBody(barPoly), ...(mouthPoly ? [toBody(mouthPoly)] : [])], hex(one.eyeColour));
|
|
59
|
+
}
|
|
60
|
+
drawLight(cv, toBody, bar, head.light ?? -1);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The eye fills with light: a band rises up the bar behind a pale front, holds, and leaves through the top
|
|
64
|
+
* (agent-eye's rainbow, in One's amber). Drawn as strips so the light can grade from amber at its foot to soft amber.
|
|
65
|
+
*/
|
|
66
|
+
function drawLight(cv, toBody, bar, light) {
|
|
67
|
+
const amount = 1 - Math.min(1, Math.abs(light));
|
|
68
|
+
if (amount <= 0.001)
|
|
69
|
+
return;
|
|
70
|
+
const top = bar.y - bar.h / 2, bottom = bar.y + bar.h / 2, H = bar.h;
|
|
71
|
+
// rising: the band's top climbs from the foot; leaving: its foot follows it out of the top.
|
|
72
|
+
const bandTop = light < 0 ? bottom - H * (1 + light) * 1.02 : top - 2;
|
|
73
|
+
const bandBottom = light < 0 ? bottom + 2 : bottom - H * light * 1.02;
|
|
74
|
+
const shape = roundRect(bar.x, bar.y, bar.w, bar.h, Math.min(GEOMETRY.cornerRadius, bar.h / 2, bar.w / 2));
|
|
75
|
+
const amber = hex(AMBER.amber), soft = hex(AMBER.soft), front = hex('#fff1d6');
|
|
76
|
+
const strips = 10;
|
|
77
|
+
for (let i = 0; i < strips; i++) {
|
|
78
|
+
const y0 = bandTop + ((bandBottom - bandTop) * i) / strips, y1 = bandTop + ((bandBottom - bandTop) * (i + 1)) / strips;
|
|
79
|
+
const part = clipY(shape, Math.max(top, y0), Math.min(bottom, y1));
|
|
80
|
+
if (part.length < 3)
|
|
81
|
+
continue;
|
|
82
|
+
const k = i / (strips - 1);
|
|
83
|
+
const c = light < 0 && i === 0 ? mix(soft, front, 0.7) : mix(soft, amber, k);
|
|
84
|
+
cv.fill([toBody(part)], c, undefined, 'nonzero', Math.min(1, amount * 1.15));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export const LOGO_ASPECT = LOGOTYPE.width / LOGOTYPE.height;
|
|
88
|
+
export const LOGO_REST = { ring: 1, fill: 0, head: null, n: 1, e: 1 };
|
|
89
|
+
/** The hybrid ONE, `px` tall (cap height plus the O's overshoot), its top-left at (x, y) in canvas pixels. */
|
|
90
|
+
export function drawLogotype(cv, x, y, px, s, tone, hint = px <= 40) {
|
|
91
|
+
// Hinting for text cells: the scale makes a stem (11 units) a whole number of pixels, which also puts the N's second
|
|
92
|
+
// stem (99 units in) on the grid; the cap height starts on a whole pixel; and each letter moves, never more than half
|
|
93
|
+
// a pixel, so its first stem's left edge does too. Stems then read as solid columns instead of two grey halves.
|
|
94
|
+
const k = hint ? Math.max(1, Math.round((11 * px) / LOGOTYPE.height)) / 11 : px / LOGOTYPE.height;
|
|
95
|
+
// …and the cap height is a whole number of pixels too (a vertical squeeze of at most half a pixel), so the baseline
|
|
96
|
+
// is as sharp as the cap line.
|
|
97
|
+
const ky = hint ? Math.round(100 * k) / 100 : k;
|
|
98
|
+
y += hint ? (px - LOGOTYPE.height * ky) / 2 : 0;
|
|
99
|
+
const dy = hint ? Math.round(y - LOGOTYPE.top * ky) - (y - LOGOTYPE.top * ky) : 0;
|
|
100
|
+
const snapX = (ux) => hint ? (Math.round(x + ux * k) - (x + ux * k)) / k : 0;
|
|
101
|
+
const base = mul(translate(x, y + dy), mul(scale(k, ky), translate(0, -LOGOTYPE.top)));
|
|
102
|
+
const ink = inkFor(tone);
|
|
103
|
+
const { cx, cy, r, inner } = LOGOTYPE.O;
|
|
104
|
+
if (s.head) {
|
|
105
|
+
drawHead(cv, mul(base, mul(translate(cx, cy), scale(r / 100))), s.head, tone);
|
|
106
|
+
}
|
|
107
|
+
else if (s.ring > 0) {
|
|
108
|
+
const hole = inner * (1 - s.fill);
|
|
109
|
+
const sweep = Math.min(1, s.ring) * Math.PI * 2;
|
|
110
|
+
const n = 96;
|
|
111
|
+
const ring = [];
|
|
112
|
+
const steps = Math.max(2, Math.ceil((n * sweep) / (Math.PI * 2)));
|
|
113
|
+
for (let i = 0; i <= steps; i++) {
|
|
114
|
+
const a = (sweep * i) / steps;
|
|
115
|
+
ring.push([cx + r * Math.sin(a), cy - r * Math.cos(a)]);
|
|
116
|
+
}
|
|
117
|
+
if (hole > 0.05)
|
|
118
|
+
for (let i = steps; i >= 0; i--) {
|
|
119
|
+
const a = (sweep * i) / steps;
|
|
120
|
+
ring.push([cx + hole * Math.sin(a), cy - hole * Math.cos(a)]);
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
ring.push([cx, cy]);
|
|
124
|
+
cv.fill([ring], s.fill > 0.98 ? discFor(tone) : ink, base);
|
|
125
|
+
}
|
|
126
|
+
const letter = (d, ox, t) => {
|
|
127
|
+
if (t <= 0)
|
|
128
|
+
return;
|
|
129
|
+
const rise = (1 - t) * 14;
|
|
130
|
+
cv.fill(parsePath(d), ink, mul(base, translate(ox, rise)), 'nonzero', Math.min(1, t * 1.25));
|
|
131
|
+
};
|
|
132
|
+
letter(LOGOTYPE.N.d, LOGOTYPE.N.x + snapX(LOGOTYPE.N.x), s.n);
|
|
133
|
+
letter(LOGOTYPE.E.d, LOGOTYPE.E.x + snapX(LOGOTYPE.E.x), s.e);
|
|
134
|
+
}
|
|
135
|
+
/** Digital One, `px` across, centred at (cx, cy). */
|
|
136
|
+
export function drawEmblem(cv, cx, cy, px, head, tone) {
|
|
137
|
+
drawHead(cv, mul(translate(cx, cy), scale(px / 200)), { one: null, pose: REST, ...head }, tone);
|
|
138
|
+
}
|
|
139
|
+
export { ONES, LIGHT, sample };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One's brand geometry and colour, for the terminal. Generated from the One site (l1felabs one-website, worktree
|
|
3
|
+
* one-ones-shapes-20261016 at cdf3a16c): components/ui/logo.tsx (EMBLEM), components/ui/logotype.ts (the hybrid ONE,
|
|
4
|
+
* JR-B103), tests/fixtures/ones-shapes/shapes.json + palette.json (the Ones, life/ones-shapes/v2) and app/one-system.css
|
|
5
|
+
* (amber, the disc, paper and night). Do not edit by hand; regenerate from those files.
|
|
6
|
+
*
|
|
7
|
+
* Rules carried over (one-system.css): amber is One's light, never the interface's; the Ones' colours belong to the
|
|
8
|
+
* characters, never to success, warning or failure; the disc is One's black on light and chalk on dark, with no glow.
|
|
9
|
+
*/
|
|
10
|
+
export declare const EMBLEM_PATH = "M16 0A16 16 0 1 1 16 32A16 16 0 1 1 16 0Z";
|
|
11
|
+
export declare const DISC: {
|
|
12
|
+
readonly black: "#141b16";
|
|
13
|
+
readonly chalk: "#f2f1ea";
|
|
14
|
+
readonly eyeLight: "#fbfaf6";
|
|
15
|
+
readonly eyeDark: "#f2f1ea";
|
|
16
|
+
};
|
|
17
|
+
export declare const AMBER: {
|
|
18
|
+
readonly amber: "#c8793a";
|
|
19
|
+
readonly soft: "#e9a867";
|
|
20
|
+
};
|
|
21
|
+
export declare const PAPER = "#f4f2ec", NIGHT = "#0b0c0c", INK = "#191614", NIGHT_INK = "#f2f1ea";
|
|
22
|
+
export declare const LOGOTYPE: {
|
|
23
|
+
readonly width: 340.5;
|
|
24
|
+
readonly height: 103;
|
|
25
|
+
readonly top: -1.5;
|
|
26
|
+
readonly O: {
|
|
27
|
+
readonly cx: 51.5;
|
|
28
|
+
readonly cy: 50;
|
|
29
|
+
readonly r: 51.5;
|
|
30
|
+
readonly inner: 39.84;
|
|
31
|
+
};
|
|
32
|
+
readonly N: {
|
|
33
|
+
readonly x: 119.5;
|
|
34
|
+
readonly d: "M0 0h11v100h-11ZM99 0h11v100h-11ZM16.1 10L31.444 10L93.9 90L78.556 90Z";
|
|
35
|
+
};
|
|
36
|
+
readonly E: {
|
|
37
|
+
readonly x: 248.5;
|
|
38
|
+
readonly d: "M0 0h11v100h-11ZM17 0h72.32v10.12h-72.32ZM0 43.94h76.816v10.12h-76.816ZM17 89.88h75v10.12h-75Z";
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export interface OneShape {
|
|
42
|
+
id: string;
|
|
43
|
+
d: string;
|
|
44
|
+
eye: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
scale: number;
|
|
48
|
+
openScale: number;
|
|
49
|
+
lookGain: number;
|
|
50
|
+
};
|
|
51
|
+
colour: string;
|
|
52
|
+
eyeColour: string;
|
|
53
|
+
keyline: string | null;
|
|
54
|
+
}
|
|
55
|
+
/** Ones that join the parade (shapes.json parade: true), in agent-eye's -100…100 head box. */
|
|
56
|
+
export declare const ONES: readonly OneShape[];
|