@pulsarinteractive/geniustasker 0.1.0-beta.1 → 0.1.0-beta.10
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/CHANGELOG.md +80 -0
- package/CONTRIBUTING.md +72 -0
- package/LICENSE +21 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +47 -370
- package/SECURITY.md +14 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/docs/ARCHITECTURE.md +144 -0
- package/docs/CHATGPT.md +86 -0
- package/docs/CLI.md +444 -0
- package/docs/DOCUMENTATION_SYNC.md +144 -0
- package/docs/INTEGRATIONS.md +62 -0
- package/docs/LIMITS.md +62 -0
- package/docs/MEMORY.md +38 -0
- package/docs/OPENAI.md +21 -0
- package/docs/OPENAI_PUBLICATION.md +22 -0
- package/docs/PLANNING.md +79 -0
- package/docs/PROJECT_FILTERS.md +77 -0
- package/docs/RELEASING.md +23 -0
- package/docs/TESTING.md +52 -0
- package/docs/WORKFLOWS.md +37 -0
- package/examples/codex.toml +3 -0
- package/examples/mcp.json +8 -0
- package/mcp.json +10 -0
- package/package.json +45 -9
- package/plugin.json +24 -0
- package/remote/tool-catalog.json +1374 -0
- package/remote/workflows.json +67 -0
- package/skills/geniustasker/SKILL.md +49 -116
- package/skills/geniustasker/agents/openai.yaml +4 -0
- package/skills/geniustasker/references/collaboration.md +70 -0
- package/skills/geniustasker/references/incremental-reads.md +16 -0
- package/skills/geniustasker/references/profiles.md +22 -0
- package/skills/geniustasker/references/project-reads.md +43 -0
- package/skills/geniustasker-documentation/SKILL.md +39 -0
- package/skills/geniustasker-documentation/agents/openai.yaml +4 -0
- package/skills/geniustasker-documentation/references/structure.md +29 -0
- package/skills/geniustasker-graph/SKILL.md +31 -0
- package/skills/geniustasker-graph/agents/openai.yaml +4 -0
- package/skills/geniustasker-media/SKILL.md +33 -0
- package/skills/geniustasker-media/agents/openai.yaml +4 -0
- package/skills/geniustasker-memory/SKILL.md +120 -0
- package/skills/geniustasker-memory/agents/openai.yaml +4 -0
- package/skills/geniustasker-memory/references/knowledge-workflows.md +57 -0
- package/skills/geniustasker-planning/SKILL.md +28 -0
- package/skills/geniustasker-planning/agents/openai.yaml +4 -0
- package/src/changes.mjs +104 -28
- package/src/cli.mjs +453 -77
- package/src/client.mjs +203 -34
- package/src/command-catalog.mjs +545 -263
- package/src/commands.mjs +215 -52
- package/src/documentation-graph.mjs +251 -0
- package/src/documentation-store.mjs +589 -0
- package/src/documentation-sync.mjs +517 -0
- package/src/documentation.mjs +136 -0
- package/src/errors.mjs +55 -12
- package/src/knowledge.mjs +109 -0
- package/src/login.mjs +190 -47
- package/src/mcp.mjs +310 -43
- package/src/media.mjs +307 -0
- package/src/presentation.d.mts +1 -0
- package/src/presentation.mjs +12 -0
- package/src/profiles.mjs +123 -32
- package/src/read-cache.mjs +111 -0
- package/src/store.mjs +101 -26
- package/src/transport.mjs +165 -32
- package/src/workflows.mjs +9 -0
package/src/cli.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { workflow } from './workflows.mjs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import {
|
|
4
|
+
syncDocumentation,
|
|
5
|
+
documentationStatus,
|
|
6
|
+
unlockDocumentation,
|
|
7
|
+
} from './documentation-sync.mjs';
|
|
1
8
|
import open, { apps } from 'open';
|
|
2
9
|
import catalog from './command-catalog.mjs';
|
|
3
10
|
import { CredentialStore } from './store.mjs';
|
|
@@ -7,126 +14,495 @@ import { deviceLogin, browserLogin } from './login.mjs';
|
|
|
7
14
|
import { CommandJournal, describeCommands, readCommandFile } from './commands.mjs';
|
|
8
15
|
import { AgentProfiles } from './profiles.mjs';
|
|
9
16
|
import { readChanges, watchChanges } from './changes.mjs';
|
|
17
|
+
import { readMemoryTree, readMemoryMarkdown } from './documentation.mjs';
|
|
18
|
+
import { searchMemory, exploreGraph, resolveMemory } from './knowledge.mjs';
|
|
19
|
+
import { MediaJournal, mediaAction, downloadMedia, compareMedia } from './media.mjs';
|
|
10
20
|
import { fail, safeError, exitCode } from './errors.mjs';
|
|
11
21
|
|
|
12
22
|
export const commands = {
|
|
13
|
-
'
|
|
14
|
-
'auth
|
|
23
|
+
'workflow show': { summary: 'Read a reviewed workflow guide; no network.', options: ['topic'] },
|
|
24
|
+
'auth login': {
|
|
25
|
+
summary: 'Open browser sign-in and choose access. No tutorial or token to copy.',
|
|
26
|
+
options: ['flow', 'browser', 'no-browser', 'scope', 'interactive'],
|
|
27
|
+
},
|
|
28
|
+
'auth logout': {
|
|
29
|
+
summary: 'Revoke this terminal session on the server, then erase local credentials.',
|
|
30
|
+
options: [],
|
|
31
|
+
},
|
|
15
32
|
'auth status': { summary: 'Read current server identity and permission ceiling.', options: [] },
|
|
16
33
|
whoami: { summary: 'Read current server identity.', options: [] },
|
|
17
|
-
capabilities: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
'profiles
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
capabilities: {
|
|
35
|
+
summary: 'Discover collections, fields, limits and write availability.',
|
|
36
|
+
options: [],
|
|
37
|
+
},
|
|
38
|
+
'profiles list': {
|
|
39
|
+
summary: 'List selectable profiles in this connection within the current grant.',
|
|
40
|
+
options: [],
|
|
41
|
+
},
|
|
42
|
+
'profiles create': {
|
|
43
|
+
summary:
|
|
44
|
+
'Create a narrower profile from JSON with a UUID chosen once; never switches automatically.',
|
|
45
|
+
options: ['file'],
|
|
46
|
+
},
|
|
47
|
+
'profiles restrict': {
|
|
48
|
+
summary:
|
|
49
|
+
'Rename or narrow the current profile from JSON with its expectedRevision. Cannot broaden access.',
|
|
50
|
+
options: ['file'],
|
|
51
|
+
},
|
|
52
|
+
'profiles select': {
|
|
53
|
+
summary:
|
|
54
|
+
'Rotate this context into a selectable profile. Cannot return to broader access; use separate contexts for independent terminals.',
|
|
55
|
+
options: ['profile'],
|
|
56
|
+
},
|
|
57
|
+
'memories list': {
|
|
58
|
+
summary: 'List independent account memories; follow nextAfter even for an empty page.',
|
|
59
|
+
options: ['after'],
|
|
60
|
+
},
|
|
61
|
+
'media compare': {
|
|
62
|
+
summary: 'Compare a local file with an existing resource before uploading a replacement.',
|
|
63
|
+
options: ['scope', 'id', 'file'],
|
|
64
|
+
},
|
|
65
|
+
'media prepare': {
|
|
66
|
+
summary: 'Prepare a local private attachment; retain its operation ID.',
|
|
67
|
+
options: ['scope', 'file', 'content-type', 'epoch'],
|
|
68
|
+
},
|
|
69
|
+
'media upload': {
|
|
70
|
+
summary: 'Upload or safely retry the prepared operation.',
|
|
71
|
+
options: ['operation'],
|
|
72
|
+
},
|
|
73
|
+
'media action': {
|
|
74
|
+
summary: 'Inspect, publish, cancel or remove a private attachment.',
|
|
75
|
+
options: ['scope', 'owner', 'id', 'action'],
|
|
76
|
+
},
|
|
77
|
+
'media download': {
|
|
78
|
+
summary: 'Download an authorized attachment to a new local file.',
|
|
79
|
+
options: ['scope', 'owner', 'id', 'file'],
|
|
80
|
+
},
|
|
81
|
+
'docs sync': {
|
|
82
|
+
summary: 'Refresh selected Memory/Graph sources in a managed local documentation directory.',
|
|
83
|
+
options: ['directory', 'file', 'max-files', 'max-bytes'],
|
|
84
|
+
},
|
|
85
|
+
'docs status': {
|
|
86
|
+
summary: 'Inspect the local snapshot date and selections without a network request.',
|
|
87
|
+
options: ['directory'],
|
|
88
|
+
},
|
|
89
|
+
'docs unlock': {
|
|
90
|
+
summary: 'Remove a sync lock only when its process on this host has stopped.',
|
|
91
|
+
options: ['directory'],
|
|
92
|
+
},
|
|
93
|
+
'memory tree': {
|
|
94
|
+
summary: 'Read one folder and descendant versions; unchanged validators avoid page bodies.',
|
|
95
|
+
options: ['scope', 'parent', 'after', 'epoch', 'checkpoint', 'if-none-match'],
|
|
96
|
+
},
|
|
97
|
+
'memory markdown': {
|
|
98
|
+
summary: 'Fetch one portable Markdown document with stable media IDs and source metadata.',
|
|
99
|
+
options: ['scope', 'page', 'revision', 'epoch', 'checkpoint'],
|
|
100
|
+
},
|
|
101
|
+
'memory search': {
|
|
102
|
+
summary: 'Search document blocks or Graph nodes without loading the corpus.',
|
|
103
|
+
options: ['scope', 'query', 'collection', 'after', 'epoch', 'checkpoint'],
|
|
104
|
+
},
|
|
105
|
+
'graph explore': {
|
|
106
|
+
summary: 'Read a bounded Graph neighborhood around one node.',
|
|
107
|
+
options: ['scope', 'center', 'depth', 'epoch', 'checkpoint'],
|
|
108
|
+
},
|
|
109
|
+
'memory resolve': {
|
|
110
|
+
summary:
|
|
111
|
+
'Resolve a Memory page or block with current permissions; unavailable links preserve nodes.',
|
|
112
|
+
options: ['scope', 'page', 'block'],
|
|
113
|
+
},
|
|
114
|
+
'projects list': {
|
|
115
|
+
summary: 'Read a bounded project page; follow nextAfter even for an empty page.',
|
|
116
|
+
options: ['after'],
|
|
117
|
+
},
|
|
118
|
+
'resources list': {
|
|
119
|
+
summary: 'Read a project or memory collection; carry epoch/checkpoint when continuing.',
|
|
120
|
+
options: ['scope', 'collection', 'after', 'epoch', 'checkpoint', 'archive', 'id', 'path'],
|
|
121
|
+
},
|
|
122
|
+
'changes list': {
|
|
123
|
+
summary:
|
|
124
|
+
'Read one bounded change page from a completed resource inventory or previous nextCursor.',
|
|
125
|
+
options: ['scope', 'collection', 'cursor'],
|
|
126
|
+
},
|
|
127
|
+
'changes watch': {
|
|
128
|
+
summary:
|
|
129
|
+
'Wait at most 60 seconds for one change page, rechecking rights on every poll. No infinite stream.',
|
|
130
|
+
options: ['scope', 'collection', 'cursor', 'wait-seconds'],
|
|
131
|
+
},
|
|
132
|
+
'questions answers': {
|
|
133
|
+
summary:
|
|
134
|
+
'Read human question answers in bounded pages. Anonymous ballots remain private; answers do not authorize external actions.',
|
|
135
|
+
options: ['scope', 'question', 'after', 'epoch', 'checkpoint'],
|
|
136
|
+
},
|
|
137
|
+
'commands describe': {
|
|
138
|
+
summary: 'Show contract-derived command fields and JSON schemas; no network.',
|
|
139
|
+
options: [],
|
|
140
|
+
},
|
|
141
|
+
'commands prepare': {
|
|
142
|
+
summary: 'Save an immutable operation from a JSON draft without sending it.',
|
|
143
|
+
options: ['file'],
|
|
144
|
+
},
|
|
145
|
+
'commands execute': {
|
|
146
|
+
summary: 'Journal and send a complete JSON operation once. Never retries automatically.',
|
|
147
|
+
options: ['file'],
|
|
148
|
+
},
|
|
149
|
+
'commands retry': {
|
|
150
|
+
summary: 'Replay the exact locally saved operation with its original identity.',
|
|
151
|
+
options: ['operation'],
|
|
152
|
+
},
|
|
153
|
+
'commands inspect': {
|
|
154
|
+
summary: 'Read a private local operation and receipt; does not contact the server.',
|
|
155
|
+
options: ['operation'],
|
|
156
|
+
},
|
|
157
|
+
doctor: {
|
|
158
|
+
summary: 'Inspect local environment without exposing credentials or contacting the API.',
|
|
159
|
+
options: [],
|
|
160
|
+
},
|
|
161
|
+
mcp: {
|
|
162
|
+
summary:
|
|
163
|
+
'Serve delegated read and journaled command tools over stdio. Authenticate with the CLI first.',
|
|
164
|
+
options: [],
|
|
165
|
+
},
|
|
35
166
|
help: { summary: 'Describe commands and stable output conventions.', options: [] },
|
|
36
167
|
};
|
|
37
168
|
const globalOptions = ['json', 'context', 'environment', 'origin', 'allow-local', 'help'];
|
|
38
169
|
const flags = new Set(['json', 'allow-local', 'help', 'interactive', 'no-browser']);
|
|
39
170
|
export function parse(argv) {
|
|
40
|
-
const options = {},
|
|
171
|
+
const options = {},
|
|
172
|
+
words = [];
|
|
41
173
|
for (let i = 0; i < argv.length; i++) {
|
|
42
174
|
const arg = argv[i];
|
|
43
|
-
if (!arg.startsWith('--')) {
|
|
175
|
+
if (!arg.startsWith('--')) {
|
|
176
|
+
if (arg.startsWith('-')) {
|
|
177
|
+
fail('invalid_arguments', 'Use long options from help.');
|
|
178
|
+
}
|
|
179
|
+
words.push(arg);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
44
182
|
const key = arg.slice(2);
|
|
45
|
-
if (!key || key.includes('=') || Object.hasOwn(options, key))
|
|
46
|
-
|
|
47
|
-
|
|
183
|
+
if (!key || key.includes('=') || Object.hasOwn(options, key)) {
|
|
184
|
+
fail('invalid_arguments', 'Options must be unique and use --name value syntax.');
|
|
185
|
+
}
|
|
186
|
+
if (flags.has(key)) {
|
|
187
|
+
options[key] = true;
|
|
188
|
+
} else {
|
|
48
189
|
const value = argv[++i];
|
|
49
|
-
if (!value || value.startsWith('--') || value.length > 4096)
|
|
190
|
+
if (!value || value.startsWith('--') || value.length > 4096) {
|
|
191
|
+
fail('invalid_arguments', 'An option value is missing or too long.');
|
|
192
|
+
}
|
|
50
193
|
options[key] = value;
|
|
51
194
|
}
|
|
52
195
|
}
|
|
53
196
|
const command = words.join(' ') || 'help';
|
|
54
197
|
const definition = commands[command];
|
|
55
|
-
if (
|
|
198
|
+
if (
|
|
199
|
+
!definition ||
|
|
200
|
+
Object.keys(options).some(
|
|
201
|
+
(key) => !globalOptions.includes(key) && !definition.options.includes(key),
|
|
202
|
+
)
|
|
203
|
+
) {
|
|
56
204
|
fail('invalid_arguments', 'Unknown command or option. Run help.');
|
|
205
|
+
}
|
|
57
206
|
return { command, options };
|
|
58
207
|
}
|
|
59
208
|
export function help(command = 'help') {
|
|
60
|
-
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
209
|
+
return {
|
|
210
|
+
formatVersion: 1,
|
|
211
|
+
command,
|
|
212
|
+
commands: command === 'help' ? commands : { [command]: commands[command] },
|
|
213
|
+
globalOptions: {
|
|
214
|
+
context: 'default; use separate names for separate accounts or agent sessions',
|
|
215
|
+
environment: 'development (beta default) | production',
|
|
216
|
+
origin: 'explicit loopback origin with --allow-local, for local tests only',
|
|
217
|
+
json: 'one versioned JSON result on stdout; progress/errors use stderr',
|
|
218
|
+
},
|
|
219
|
+
loginOptions: {
|
|
220
|
+
flow: 'browser (default, PKCE loopback) | device (SSH/headless)',
|
|
221
|
+
browser: 'default (automatic) | chrome; --no-browser prints the link without opening it',
|
|
222
|
+
scope:
|
|
223
|
+
'space-separated permission ceiling; browser defaults to read-only and requires explicit write selection',
|
|
224
|
+
interactive:
|
|
225
|
+
'required when starting login without a terminal; never use to bypass human consent',
|
|
226
|
+
},
|
|
227
|
+
exitCodes: {
|
|
228
|
+
0: 'success',
|
|
229
|
+
1: 'failure',
|
|
230
|
+
2: 'usage',
|
|
231
|
+
3: 'authentication',
|
|
232
|
+
4: 'permission',
|
|
233
|
+
5: 'conflict',
|
|
234
|
+
6: 'rate limit',
|
|
235
|
+
7: 'transient failure',
|
|
236
|
+
},
|
|
237
|
+
examples: [
|
|
238
|
+
'geniustasker auth login --environment development --context work --browser chrome',
|
|
239
|
+
'geniustasker projects list --environment development --context work --json',
|
|
240
|
+
'geniustasker resources list --scope project:ID --collection tasks --json',
|
|
241
|
+
],
|
|
242
|
+
privacy:
|
|
243
|
+
'Do not put tokens in arguments, environment variables, tickets, logs or MCP configuration. This beta uses the development environment by default; access still requires an admitted account.',
|
|
244
|
+
};
|
|
66
245
|
}
|
|
67
246
|
async function openConsent(url, browser) {
|
|
68
247
|
await open(url, { wait: false, ...(browser === 'chrome' ? { app: { name: apps.chrome } } : {}) });
|
|
69
248
|
}
|
|
70
|
-
export async function main(
|
|
249
|
+
export async function main(
|
|
250
|
+
argv,
|
|
251
|
+
{
|
|
252
|
+
stdout = process.stdout,
|
|
253
|
+
stderr = process.stderr,
|
|
254
|
+
isTTY = process.stdin.isTTY,
|
|
255
|
+
directory,
|
|
256
|
+
fetchImpl,
|
|
257
|
+
signal,
|
|
258
|
+
openBrowserImpl = openConsent,
|
|
259
|
+
} = {},
|
|
260
|
+
) {
|
|
71
261
|
try {
|
|
72
262
|
const { command, options } = parse(argv);
|
|
73
|
-
const emit =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
263
|
+
const emit = (data) =>
|
|
264
|
+
stdout.write(
|
|
265
|
+
JSON.stringify({ formatVersion: 1, ok: true, data }, null, options.json ? 0 : 2) + '\n',
|
|
266
|
+
);
|
|
267
|
+
if (command === 'help' || options.help) {
|
|
268
|
+
emit(help(command));
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
if (options.environment && !Object.hasOwn(ORIGINS, options.environment)) {
|
|
272
|
+
fail('invalid_arguments', 'Environment must be production or development.');
|
|
273
|
+
}
|
|
274
|
+
if (options.origin && options.environment) {
|
|
275
|
+
fail('invalid_arguments', 'Choose --environment or --origin, not both.');
|
|
276
|
+
}
|
|
277
|
+
const origin = originOf(
|
|
278
|
+
options.origin ?? ORIGINS[options.environment ?? 'development'],
|
|
279
|
+
options['allow-local'],
|
|
280
|
+
);
|
|
78
281
|
const store = new CredentialStore({ context: options.context, directory });
|
|
79
282
|
const client = new AgentClient(new Transport(origin, { fetchImpl, signal }), store);
|
|
80
283
|
let data;
|
|
81
284
|
if (command === 'auth login') {
|
|
82
|
-
if (!isTTY && !options.interactive)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
285
|
+
if (!isTTY && !options.interactive) {
|
|
286
|
+
fail('invalid_arguments', 'Login needs an interactive human.', {
|
|
287
|
+
remediation:
|
|
288
|
+
'Run auth login in a terminal. Automated clients must use an existing connection.',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
(options.flow && !['device', 'browser'].includes(options.flow)) ||
|
|
293
|
+
(options.browser && !['chrome', 'default'].includes(options.browser)) ||
|
|
294
|
+
(options.browser && options['no-browser'])
|
|
295
|
+
) {
|
|
296
|
+
fail(
|
|
297
|
+
'invalid_arguments',
|
|
298
|
+
'Use --flow browser|device, with --browser default|chrome or --no-browser.',
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const notify = (event) => {
|
|
302
|
+
if (options.json) {
|
|
303
|
+
stderr.write(JSON.stringify({ formatVersion: 1, ...event }) + '\n');
|
|
304
|
+
} else if (event.event === 'consent_required') {
|
|
305
|
+
stderr.write(
|
|
306
|
+
`\nGeniusTasker · Connect your agent\n\n${options['no-browser'] ? 'Open this link' : 'Opening your browser. If it does not open, use this link'}:\n\n${event.url}\n${event.userCode ? `\nDevice code: ${event.userCode}\n` : ''}\nSign in, choose access, and approve. This terminal will finish automatically.\nWaiting for your decision…\n`,
|
|
307
|
+
);
|
|
308
|
+
} else if (event.event === 'browser_unavailable') {
|
|
309
|
+
stderr.write('Could not open the browser. Use the link above; login is still waiting.\n');
|
|
310
|
+
}
|
|
88
311
|
};
|
|
89
|
-
data = await (options.flow === 'device' ? deviceLogin : browserLogin)(client, {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
312
|
+
data = await (options.flow === 'device' ? deviceLogin : browserLogin)(client, {
|
|
313
|
+
scope: options.scope ?? catalog.grants.permissions.join(' '),
|
|
314
|
+
openBrowser: options['no-browser']
|
|
315
|
+
? undefined
|
|
316
|
+
: async (url) => {
|
|
317
|
+
try {
|
|
318
|
+
await openBrowserImpl(url, options.browser);
|
|
319
|
+
} catch {
|
|
320
|
+
notify({
|
|
321
|
+
event: 'browser_unavailable',
|
|
322
|
+
message: 'Open the printed consent URL in your browser. Login is still waiting.',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
notify,
|
|
327
|
+
});
|
|
94
328
|
if (!options.json) {
|
|
95
|
-
stdout.write(
|
|
329
|
+
stdout.write(
|
|
330
|
+
`\nConnected to GeniusTasker.\nYour chosen access is ready for the CLI and MCP.\nRun geniustasker auth status${options.context ? ' --context ' + options.context : ''} to check the connection.\n`,
|
|
331
|
+
);
|
|
96
332
|
return 0;
|
|
97
333
|
}
|
|
98
|
-
} else if (command === 'auth logout')
|
|
99
|
-
|
|
100
|
-
else if (command === 'profiles
|
|
101
|
-
|
|
102
|
-
else if (command === 'profiles
|
|
103
|
-
|
|
104
|
-
else if (command === '
|
|
105
|
-
|
|
106
|
-
else if (command === '
|
|
107
|
-
|
|
108
|
-
else if (command === '
|
|
109
|
-
|
|
110
|
-
else if (command === '
|
|
111
|
-
|
|
112
|
-
else if (command === '
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
334
|
+
} else if (command === 'auth logout') {
|
|
335
|
+
data = await client.logout();
|
|
336
|
+
} else if (command === 'profiles list') {
|
|
337
|
+
data = await new AgentProfiles(client).list();
|
|
338
|
+
} else if (command === 'profiles create') {
|
|
339
|
+
data = await new AgentProfiles(client).create(await readCommandFile(options.file));
|
|
340
|
+
} else if (command === 'profiles restrict') {
|
|
341
|
+
data = await new AgentProfiles(client).restrict(await readCommandFile(options.file));
|
|
342
|
+
} else if (command === 'profiles select') {
|
|
343
|
+
data = await new AgentProfiles(client).select({ profileId: options.profile });
|
|
344
|
+
} else if (command === 'workflow show') {
|
|
345
|
+
data = workflow({ topic: options.topic });
|
|
346
|
+
} else if (command === 'commands describe') {
|
|
347
|
+
data = describeCommands();
|
|
348
|
+
} else if (command === 'commands prepare') {
|
|
349
|
+
data = await new CommandJournal(client).prepare(await readCommandFile(options.file));
|
|
350
|
+
} else if (command === 'commands execute') {
|
|
351
|
+
data = await new CommandJournal(client).execute(await readCommandFile(options.file));
|
|
352
|
+
} else if (command === 'commands retry') {
|
|
353
|
+
data = await new CommandJournal(client).retry(options.operation);
|
|
354
|
+
} else if (command === 'commands inspect') {
|
|
355
|
+
data = await new CommandJournal(client).inspect(options.operation);
|
|
356
|
+
} else if (command === 'doctor') {
|
|
357
|
+
data = await client.doctor();
|
|
358
|
+
} else if (command === 'whoami' || command === 'auth status') {
|
|
359
|
+
data = await client.get('me');
|
|
360
|
+
} else if (command === 'capabilities') {
|
|
361
|
+
data = await client.get('capabilities');
|
|
362
|
+
} else if (command === 'projects list' || command === 'memories list') {
|
|
363
|
+
data = await client.get(command === 'projects list' ? 'projects' : 'memories', {
|
|
364
|
+
after: options.after,
|
|
365
|
+
});
|
|
366
|
+
} else if (command === 'changes list' || command === 'changes watch') {
|
|
367
|
+
const input = {
|
|
368
|
+
scope: options.scope,
|
|
369
|
+
collection: options.collection,
|
|
370
|
+
cursor: options.cursor,
|
|
371
|
+
};
|
|
372
|
+
data =
|
|
373
|
+
command === 'changes list'
|
|
374
|
+
? await readChanges(client, input)
|
|
375
|
+
: await watchChanges(client, {
|
|
376
|
+
...input,
|
|
377
|
+
...(options['wait-seconds'] === undefined
|
|
378
|
+
? {}
|
|
379
|
+
: { waitSeconds: Number(options['wait-seconds']) }),
|
|
380
|
+
});
|
|
381
|
+
} else if (command === 'docs sync') {
|
|
382
|
+
let configuration;
|
|
383
|
+
if (options.file) {
|
|
384
|
+
const raw = await readFile(options.file, 'utf8');
|
|
385
|
+
if (Buffer.byteLength(raw) > 32768) {
|
|
386
|
+
fail('invalid_arguments', 'The source configuration is too large.');
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
configuration = JSON.parse(raw);
|
|
390
|
+
} catch {
|
|
391
|
+
fail('invalid_arguments', 'The source configuration must be JSON.');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
data = await syncDocumentation(client, {
|
|
395
|
+
directory: options.directory,
|
|
396
|
+
configuration,
|
|
397
|
+
maxFiles: options['max-files'] === undefined ? undefined : Number(options['max-files']),
|
|
398
|
+
maxBytes: options['max-bytes'] === undefined ? undefined : Number(options['max-bytes']),
|
|
399
|
+
});
|
|
400
|
+
} else if (command === 'docs status' || command === 'docs unlock') {
|
|
401
|
+
data = await (command === 'docs status' ? documentationStatus : unlockDocumentation)({
|
|
402
|
+
directory: options.directory,
|
|
403
|
+
});
|
|
404
|
+
} else if (command === 'memory tree') {
|
|
405
|
+
data = await readMemoryTree(client, {
|
|
406
|
+
scope: options.scope,
|
|
407
|
+
parentId: options.parent,
|
|
408
|
+
after: options.after,
|
|
409
|
+
epoch: options.epoch,
|
|
410
|
+
checkpoint: options.checkpoint,
|
|
411
|
+
ifNoneMatch: options['if-none-match'],
|
|
412
|
+
});
|
|
413
|
+
} else if (command === 'memory markdown') {
|
|
414
|
+
data = await readMemoryMarkdown(client, {
|
|
415
|
+
scope: options.scope,
|
|
416
|
+
pageId: options.page,
|
|
417
|
+
revision: options.revision,
|
|
418
|
+
epoch: options.epoch,
|
|
419
|
+
checkpoint: options.checkpoint,
|
|
420
|
+
});
|
|
421
|
+
} else if (command === 'media compare') {
|
|
422
|
+
data = await compareMedia(client, {
|
|
423
|
+
scope: options.scope,
|
|
424
|
+
id: options.id,
|
|
425
|
+
file: options.file,
|
|
426
|
+
});
|
|
427
|
+
} else if (command === 'media prepare') {
|
|
428
|
+
data = await new MediaJournal(client).prepare({
|
|
429
|
+
scope: options.scope,
|
|
430
|
+
file: options.file,
|
|
431
|
+
contentType: options['content-type'],
|
|
432
|
+
epoch: options.epoch,
|
|
433
|
+
});
|
|
434
|
+
} else if (command === 'media upload') {
|
|
435
|
+
data = await new MediaJournal(client).upload(options.operation);
|
|
436
|
+
} else if (command === 'media action') {
|
|
437
|
+
data = await mediaAction(client, {
|
|
438
|
+
scope: options.scope,
|
|
439
|
+
ownerId: options.owner,
|
|
440
|
+
id: options.id,
|
|
441
|
+
action: options.action,
|
|
442
|
+
});
|
|
443
|
+
} else if (command === 'media download') {
|
|
444
|
+
data = await downloadMedia(client, {
|
|
445
|
+
scope: options.scope,
|
|
446
|
+
ownerId: options.owner,
|
|
447
|
+
id: options.id,
|
|
448
|
+
file: options.file,
|
|
449
|
+
});
|
|
450
|
+
} else if (['memory search', 'graph explore', 'memory resolve'].includes(command)) {
|
|
451
|
+
const input = Object.fromEntries(
|
|
452
|
+
commands[command].options
|
|
453
|
+
.filter((key) => options[key] !== undefined)
|
|
454
|
+
.map((key) => [key, options[key]]),
|
|
455
|
+
);
|
|
456
|
+
if (command === 'memory search') {
|
|
457
|
+
data = await searchMemory(client, input);
|
|
458
|
+
} else if (command === 'graph explore') {
|
|
459
|
+
data = await exploreGraph(client, {
|
|
460
|
+
...input,
|
|
461
|
+
...(input.depth === undefined ? {} : { depth: Number(input.depth) }),
|
|
462
|
+
});
|
|
463
|
+
} else {
|
|
464
|
+
data = await resolveMemory(client, {
|
|
465
|
+
scope: input.scope,
|
|
466
|
+
pageId: input.page,
|
|
467
|
+
...(input.block === undefined ? {} : { blockId: input.block }),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
} else if (command === 'resources list' || command === 'questions answers') {
|
|
471
|
+
if (
|
|
472
|
+
!(command === 'questions answers'
|
|
473
|
+
? options.scope?.startsWith('project:')
|
|
474
|
+
: // eslint-disable-next-line no-control-regex -- Reject control characters in untrusted input.
|
|
475
|
+
/^(project|memory):[^\u0000-\u001f]+$/.test(options.scope ?? '')) ||
|
|
476
|
+
!(command === 'resources list' ? options.collection : options.question) ||
|
|
477
|
+
(options.after && (!options.epoch || !options.checkpoint))
|
|
478
|
+
) {
|
|
479
|
+
fail(
|
|
480
|
+
'invalid_arguments',
|
|
481
|
+
'Use --scope project:ID or memory:ID and --collection for resources or --question for answers; continuation needs --epoch and --checkpoint.',
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
data = await client.get(
|
|
485
|
+
command === 'resources list' ? 'resources' : 'answers',
|
|
486
|
+
Object.fromEntries(
|
|
487
|
+
commands[command].options
|
|
488
|
+
.filter((key) => options[key] !== undefined)
|
|
489
|
+
.map((key) => [key, options[key]]),
|
|
490
|
+
),
|
|
491
|
+
);
|
|
121
492
|
} else if (command === 'mcp') {
|
|
122
493
|
const { runMcp } = await import('./mcp.mjs');
|
|
123
494
|
const server = await runMcp(client);
|
|
124
|
-
const close = () => {
|
|
495
|
+
const close = () => {
|
|
496
|
+
void server.close();
|
|
497
|
+
};
|
|
125
498
|
signal?.addEventListener('abort', close, { once: true });
|
|
126
|
-
if (signal?.aborted)
|
|
499
|
+
if (signal?.aborted) {
|
|
500
|
+
close();
|
|
501
|
+
}
|
|
127
502
|
return 0; // stdout belongs exclusively to JSON-RPC.
|
|
128
503
|
}
|
|
129
|
-
emit(data);
|
|
504
|
+
emit(data);
|
|
505
|
+
return 0;
|
|
130
506
|
} catch (error) {
|
|
131
507
|
const safe = safeError(error);
|
|
132
508
|
stderr.write(JSON.stringify({ formatVersion: 1, ok: false, error: safe.toJSON() }) + '\n');
|