@vforsh/argus 0.4.0 → 0.5.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 (36) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/argus.js +1108 -558
  3. package/dist/cli/program.d.ts +11 -1
  4. package/dist/cli/program.d.ts.map +1 -1
  5. package/dist/cli/program.js +14 -9
  6. package/dist/cli/program.js.map +1 -1
  7. package/dist/cli/register/index.d.ts.map +1 -1
  8. package/dist/cli/register/index.js +2 -0
  9. package/dist/cli/register/index.js.map +1 -1
  10. package/dist/cli/register/sessionCommands.d.ts +3 -0
  11. package/dist/cli/register/sessionCommands.d.ts.map +1 -0
  12. package/dist/cli/register/sessionCommands.js +24 -0
  13. package/dist/cli/register/sessionCommands.js.map +1 -0
  14. package/dist/output/io.d.ts +7 -0
  15. package/dist/output/io.d.ts.map +1 -1
  16. package/dist/output/io.js +21 -2
  17. package/dist/output/io.js.map +1 -1
  18. package/dist/session/runSession.d.ts +15 -0
  19. package/dist/session/runSession.d.ts.map +1 -0
  20. package/dist/session/runSession.js +184 -0
  21. package/dist/session/runSession.js.map +1 -0
  22. package/dist/session/sessionArgv.d.ts +42 -0
  23. package/dist/session/sessionArgv.d.ts.map +1 -0
  24. package/dist/session/sessionArgv.js +224 -0
  25. package/dist/session/sessionArgv.js.map +1 -0
  26. package/dist/session/sessionDispatch.d.ts +21 -0
  27. package/dist/session/sessionDispatch.d.ts.map +1 -0
  28. package/dist/session/sessionDispatch.js +137 -0
  29. package/dist/session/sessionDispatch.js.map +1 -0
  30. package/dist/session/stdioCapture.d.ts +33 -0
  31. package/dist/session/stdioCapture.d.ts.map +1 -0
  32. package/dist/session/stdioCapture.js +56 -0
  33. package/dist/session/stdioCapture.js.map +1 -0
  34. package/dist/skill/argus/SKILL.md +29 -0
  35. package/dist/skill/argus/reference/SESSION.md +153 -0
  36. package/package.json +5 -5
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Resolve a space-separated `cmd` against the registered command tree.
3
+ *
4
+ * Aliases resolve like they do on the command line (`js` → `eval`, `ext` → `extension`),
5
+ * so a host can paste the command it already types.
6
+ */
7
+ export const resolveSessionCommand = (program, cmd) => {
8
+ const tokens = cmd.trim().split(/\s+/).filter(Boolean);
9
+ if (tokens.length === 0) {
10
+ return { ok: false, code: 'session_invalid_request', message: 'cmd must name a command' };
11
+ }
12
+ let current = program;
13
+ const path = [];
14
+ for (const token of tokens) {
15
+ const next = current.commands.find((candidate) => candidate.name() === token || candidate.aliases().includes(token));
16
+ if (!next) {
17
+ const context = path.length > 0 ? ` under "${path.join(' ')}"` : '';
18
+ return { ok: false, code: 'session_unknown_command', message: `Unknown command "${token}"${context}.` };
19
+ }
20
+ current = next;
21
+ path.push(next.name());
22
+ }
23
+ return { ok: true, command: current, path };
24
+ };
25
+ /**
26
+ * Turn one session request into the argv the CLI would have been spawned with.
27
+ *
28
+ * Everything is derived from the command's own Commander definition rather than a
29
+ * hand-kept table: option names, whether an option takes a value, which positionals exist,
30
+ * and whether `--json` is even declared. A command added elsewhere in the CLI — or by a
31
+ * plugin — becomes reachable over the session with no change here.
32
+ */
33
+ export const buildSessionArgv = (input) => {
34
+ const resolved = resolveSessionCommand(input.program, input.request.cmd);
35
+ if (!resolved.ok) {
36
+ return resolved;
37
+ }
38
+ const blocked = rejectNonSessionCommand(resolved.path);
39
+ if (blocked) {
40
+ return blocked;
41
+ }
42
+ const tail = input.request.argv ? [...input.request.argv] : buildArgvFromArgs(resolved.command, input.request.args ?? {});
43
+ if (isArgvFailure(tail)) {
44
+ return tail;
45
+ }
46
+ const argv = [...resolved.path];
47
+ if (takesWatcherId(resolved.command) && tail[0] !== input.watcherId) {
48
+ argv.push(input.watcherId);
49
+ }
50
+ argv.push(...tail);
51
+ if (declaresOption(resolved.command, 'json') && !tail.some(isJsonFlag)) {
52
+ argv.push('--json');
53
+ }
54
+ const rejected = rejectStdinInput(argv, resolved.path);
55
+ if (rejected) {
56
+ return rejected;
57
+ }
58
+ return { ok: true, argv, resolved };
59
+ };
60
+ /**
61
+ * Commands that never return on their own, and so cannot be a request/response pair.
62
+ *
63
+ * Daemons (`start`, `watcher start`) also call `process.exit` directly, which would take the
64
+ * session down with them; the tails block until interrupted. Run these as their own process
65
+ * and drive the resulting watcher from the session.
66
+ */
67
+ const NON_SESSION_COMMANDS = new Set(['session', 'start', 'chrome start', 'watcher start', 'watcher native-host', 'logs tail', 'net tail', 'net sse']);
68
+ const rejectNonSessionCommand = (path) => {
69
+ const name = path.join(' ');
70
+ if (!NON_SESSION_COMMANDS.has(name)) {
71
+ return null;
72
+ }
73
+ return {
74
+ ok: false,
75
+ code: 'session_command_rejected',
76
+ message: `"${name}" runs until interrupted and cannot be dispatched from a session; run it as its own process.`,
77
+ };
78
+ };
79
+ /**
80
+ * Reject the two spellings that would read the stream the transport owns.
81
+ *
82
+ * `-` only means "read stdin" for the eval commands; elsewhere it is an ordinary value, so
83
+ * the check is scoped rather than applied to every argv.
84
+ */
85
+ const rejectStdinInput = (argv, path) => {
86
+ const readsStdin = argv.includes('--stdin') || (STDIN_DASH_COMMANDS.has(path[0]) && argv.includes('-'));
87
+ if (!readsStdin) {
88
+ return null;
89
+ }
90
+ return {
91
+ ok: false,
92
+ code: 'session_command_rejected',
93
+ message: 'stdin belongs to the session transport; pass the expression inline or with --file instead of reading stdin.',
94
+ };
95
+ };
96
+ const STDIN_DASH_COMMANDS = new Set(['eval', 'eval-until']);
97
+ /** Narrow the `tokens | failure` results the builders below return. */
98
+ const isArgvFailure = (value) => !Array.isArray(value);
99
+ const isJsonFlag = (token) => token === '--json' || token === '--no-json' || token === '--json-full';
100
+ /** A command whose first declared argument is the watcher id gets it injected. */
101
+ const takesWatcherId = (command) => command.registeredArguments[0]?.name() === 'id';
102
+ const declaresOption = (command, name) => findOptions(command, name).length > 0;
103
+ /**
104
+ * Map a named `args` object onto CLI tokens.
105
+ *
106
+ * Keys match an option first (by attribute name, long flag, or short flag) and a declared
107
+ * positional argument second, which is the same precedence the one-shot CLI applies when a
108
+ * command offers both spellings (`argus eval app "1+1"` vs `argus eval app --expression "1+1"`).
109
+ */
110
+ const buildArgvFromArgs = (command, args) => {
111
+ const names = positionalNames(command);
112
+ const tokens = [];
113
+ const positionals = {};
114
+ for (const [key, value] of Object.entries(args)) {
115
+ if (value === undefined)
116
+ continue;
117
+ const options = findOptions(command, key);
118
+ if (options.length === 0) {
119
+ if (!names.includes(camelCase(key))) {
120
+ const path = commandPath(command);
121
+ return {
122
+ ok: false,
123
+ code: 'session_invalid_request',
124
+ message: `Unknown argument "${key}" for command "${path}". Run \`argus ${path} --help\` for the accepted flags.`,
125
+ };
126
+ }
127
+ positionals[camelCase(key)] = value;
128
+ continue;
129
+ }
130
+ const emitted = emitOption(options, key, value);
131
+ if (isArgvFailure(emitted)) {
132
+ return emitted;
133
+ }
134
+ tokens.push(...emitted);
135
+ }
136
+ const ordered = orderPositionals(names, positionals);
137
+ if (isArgvFailure(ordered)) {
138
+ return ordered;
139
+ }
140
+ return [...ordered, ...tokens];
141
+ };
142
+ /** Render one `args` entry as CLI tokens, honoring value/boolean/negated/repeatable shapes. */
143
+ const emitOption = (options, key, value) => {
144
+ const positive = options.find((option) => !option.negate);
145
+ const negated = options.find((option) => option.negate);
146
+ const valued = options.find((option) => option.required || option.optional);
147
+ if (valued) {
148
+ const values = Array.isArray(value) ? value : [value];
149
+ const tokens = [];
150
+ for (const entry of values) {
151
+ if (entry == null)
152
+ continue;
153
+ if (typeof entry === 'object') {
154
+ return { ok: false, code: 'session_invalid_request', message: `Argument "${key}" must be a string, number, or boolean.` };
155
+ }
156
+ tokens.push(flagOf(valued), String(entry));
157
+ }
158
+ return tokens;
159
+ }
160
+ if (typeof value !== 'boolean') {
161
+ return { ok: false, code: 'session_invalid_request', message: `Argument "${key}" is a switch and must be true or false.` };
162
+ }
163
+ // A switch set to its declared default needs no token: `{ bundle: false }` on a command
164
+ // that only declares `--bundle` is already the default, and so is `{ await: true }` on one
165
+ // that only declares `--no-await`.
166
+ const wanted = value ? positive : negated;
167
+ return wanted ? [flagOf(wanted)] : [];
168
+ };
169
+ /** Place named positionals into declaration order, rejecting gaps a CLI could not express. */
170
+ const orderPositionals = (declaredNames, values) => {
171
+ const tokens = [];
172
+ let missing = null;
173
+ for (const name of declaredNames) {
174
+ if (name === 'id')
175
+ continue;
176
+ const value = values[name];
177
+ if (value === undefined) {
178
+ missing ??= name;
179
+ continue;
180
+ }
181
+ if (missing) {
182
+ return {
183
+ ok: false,
184
+ code: 'session_invalid_request',
185
+ message: `Argument "${name}" cannot be set without "${missing}"; positional arguments are filled in order.`,
186
+ };
187
+ }
188
+ for (const entry of Array.isArray(value) ? value : [value]) {
189
+ if (entry != null && typeof entry === 'object') {
190
+ return { ok: false, code: 'session_invalid_request', message: `Argument "${name}" must be a string, number, or boolean.` };
191
+ }
192
+ tokens.push(String(entry));
193
+ }
194
+ }
195
+ return tokens;
196
+ };
197
+ /** Every option named `key` on the command or its ancestors, excluding the root program. */
198
+ const findOptions = (command, key) => {
199
+ const wanted = camelCase(key);
200
+ return commandChain(command)
201
+ .flatMap((current) => current.options)
202
+ .filter((option) => option.attributeName() === wanted || option.name() === key || option.short === `-${key}`);
203
+ };
204
+ const positionalNames = (command) => command.registeredArguments.map((argument) => camelCase(argument.name()));
205
+ const flagOf = (option) => option.long ?? option.short ?? `--${option.name()}`;
206
+ const commandPath = (command) => commandChain(command)
207
+ .map((current) => current.name())
208
+ .reverse()
209
+ .join(' ');
210
+ /**
211
+ * The command and its ancestors, nearest first, stopping before the root program.
212
+ *
213
+ * Program-level flags (`--plugin`) belong to a one-shot invocation, not to a request the
214
+ * session dispatches, so they stay out of both name resolution and error messages.
215
+ */
216
+ const commandChain = (command) => {
217
+ const chain = [];
218
+ for (let current = command; current?.parent; current = current.parent) {
219
+ chain.push(current);
220
+ }
221
+ return chain;
222
+ };
223
+ const camelCase = (value) => value.replace(/[-_]([a-z0-9])/g, (_, character) => character.toUpperCase());
224
+ //# sourceMappingURL=sessionArgv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionArgv.js","sourceRoot":"","sources":["../../src/session/sessionArgv.ts"],"names":[],"mappings":"AAwBA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAgB,EAAE,GAAW,EAA+C,EAAE;IACnH,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACtD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAA;IAC1F,CAAC;IAED,IAAI,OAAO,GAAG,OAAO,CAAA;IACrB,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;QACpH,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;YACnE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,oBAAoB,KAAK,IAAI,OAAO,GAAG,EAAE,CAAA;QACxG,CAAC;QACD,OAAO,GAAG,IAAI,CAAA;QACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACvB,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC5C,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAKhC,EAA2C,EAAE;IAC7C,MAAM,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACxE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO,QAAQ,CAAA;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtD,IAAI,OAAO,EAAE,CAAC;QACb,OAAO,OAAO,CAAA;IACf,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IACzH,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC/B,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC3B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;IAElB,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACpB,CAAC;IAED,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtD,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO,QAAQ,CAAA;IAChB,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;AACpC,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,qBAAqB,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAA;AAEtJ,MAAM,uBAAuB,GAAG,CAAC,IAAuB,EAA6B,EAAE;IACtF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,CAAA;IACZ,CAAC;IACD,OAAO;QACN,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,IAAI,IAAI,8FAA8F;KAC/G,CAAA;AACF,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,CAAC,IAAuB,EAAE,IAAuB,EAA6B,EAAE;IACxG,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;IACvG,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,IAAI,CAAA;IACZ,CAAC;IACD,OAAO;QACN,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,6GAA6G;KACtH,CAAA;AACF,CAAC,CAAA;AAED,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAA;AAE3D,uEAAuE;AACvE,MAAM,aAAa,GAAG,CAAC,KAAoC,EAA+B,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAElH,MAAM,UAAU,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,aAAa,CAAA;AAErH,kFAAkF;AAClF,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAW,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI,CAAA;AAErG,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAE,IAAY,EAAW,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;AAEzG;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,CAAC,OAAgB,EAAE,IAA6B,EAAiC,EAAE;IAC5G,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,WAAW,GAA4B,EAAE,CAAA;IAE/C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QAEjC,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QACzC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;gBACjC,OAAO;oBACN,EAAE,EAAE,KAAK;oBACT,IAAI,EAAE,yBAAyB;oBAC/B,OAAO,EAAE,qBAAqB,GAAG,kBAAkB,IAAI,kBAAkB,IAAI,mCAAmC;iBAChH,CAAA;YACF,CAAC;YACD,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAA;YACnC,SAAQ;QACT,CAAC;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QAC/C,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAA;QACf,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;IACxB,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;IACpD,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAA;IACf,CAAC;IAED,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC,CAAA;AAC/B,CAAC,CAAA;AAED,+FAA+F;AAC/F,MAAM,UAAU,GAAG,CAAC,OAA0B,EAAE,GAAW,EAAE,KAAc,EAAiC,EAAE;IAC7G,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAA;IAE3E,IAAI,MAAM,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACrD,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC5B,IAAI,KAAK,IAAI,IAAI;gBAAE,SAAQ;YAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,aAAa,GAAG,yCAAyC,EAAE,CAAA;YAC1H,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QAC3C,CAAC;QACD,OAAO,MAAM,CAAA;IACd,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,aAAa,GAAG,0CAA0C,EAAE,CAAA;IAC3H,CAAC;IAED,wFAAwF;IACxF,2FAA2F;IAC3F,mCAAmC;IACnC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAA;IACzC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC,CAAA;AAED,8FAA8F;AAC9F,MAAM,gBAAgB,GAAG,CAAC,aAAgC,EAAE,MAA+B,EAAiC,EAAE;IAC7H,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,IAAI,OAAO,GAAkB,IAAI,CAAA;IAEjC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,IAAI;YAAE,SAAQ;QAE3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,KAAK,IAAI,CAAA;YAChB,SAAQ;QACT,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACb,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,yBAAyB;gBAC/B,OAAO,EAAE,aAAa,IAAI,4BAA4B,OAAO,8CAA8C;aAC3G,CAAA;QACF,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,aAAa,IAAI,yCAAyC,EAAE,CAAA;YAC3H,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QAC3B,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAA;AACd,CAAC,CAAA;AAED,4FAA4F;AAC5F,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAE,GAAW,EAAY,EAAE;IAC/D,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IAC7B,OAAO,YAAY,CAAC,OAAO,CAAC;SAC1B,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;SACrC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,GAAG,EAAE,CAAC,CAAA;AAC/G,CAAC,CAAA;AAED,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAY,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;AAEjI,MAAM,MAAM,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAA;AAE9F,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAU,EAAE,CAChD,YAAY,CAAC,OAAO,CAAC;KACnB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;KAChC,OAAO,EAAE;KACT,IAAI,CAAC,GAAG,CAAC,CAAA;AAEZ;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAa,EAAE;IACpD,MAAM,KAAK,GAAc,EAAE,CAAA;IAC3B,KAAK,IAAI,OAAO,GAAmB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QACvF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,SAAiB,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { type Command } from 'commander';
2
+ import type { SessionRequest, SessionResponse } from '@vforsh/argus-core';
3
+ import type { StdioCapture } from './stdioCapture.js';
4
+ export type SessionDispatchInput = {
5
+ program: Command;
6
+ capture: StdioCapture;
7
+ request: SessionRequest;
8
+ /** Watcher the session is pinned to. */
9
+ watcherId: string;
10
+ /** Watchdog applied when the request does not carry its own `timeout`. `0` disables it. */
11
+ defaultTimeoutMs: number;
12
+ };
13
+ /**
14
+ * Run one request through the real command tree and turn it into a response line.
15
+ *
16
+ * The command is the same object graph `argus <cmd>` would run — same validation, same
17
+ * `--json` payload, same exit-code conventions — so a host that already parses one-shot
18
+ * output does not have to parse anything new.
19
+ */
20
+ export declare const dispatchSessionRequest: (input: SessionDispatchInput) => Promise<SessionResponse>;
21
+ //# sourceMappingURL=sessionDispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionDispatch.d.ts","sourceRoot":"","sources":["../../src/session/sessionDispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,KAAK,EAAe,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGtF,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEpE,MAAM,MAAM,oBAAoB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,2FAA2F;IAC3F,gBAAgB,EAAE,MAAM,CAAA;CACxB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAAU,OAAO,oBAAoB,KAAG,OAAO,CAAC,eAAe,CAgDjG,CAAA"}
@@ -0,0 +1,137 @@
1
+ import { CommanderError } from 'commander';
2
+ import { formatError, parseDurationMs } from '@vforsh/argus-core';
3
+ import { buildSessionArgv } from './sessionArgv.js';
4
+ /**
5
+ * Run one request through the real command tree and turn it into a response line.
6
+ *
7
+ * The command is the same object graph `argus <cmd>` would run — same validation, same
8
+ * `--json` payload, same exit-code conventions — so a host that already parses one-shot
9
+ * output does not have to parse anything new.
10
+ */
11
+ export const dispatchSessionRequest = async (input) => {
12
+ const { request } = input;
13
+ const respond = createResponder(request, Date.now());
14
+ const timeoutMs = resolveTimeoutMs(request.timeout, input.defaultTimeoutMs);
15
+ if (timeoutMs == null) {
16
+ return respond.failure({ message: `Invalid timeout "${String(request.timeout)}".`, code: 'session_invalid_request' }, 2);
17
+ }
18
+ const built = buildSessionArgv({ program: input.program, request, watcherId: input.watcherId });
19
+ if (!built.ok) {
20
+ return respond.failure({ message: built.message, code: built.code }, 2);
21
+ }
22
+ const sink = { stdout: [], stderr: [] };
23
+ process.exitCode = 0;
24
+ const running = input.capture.run(sink, async () => {
25
+ try {
26
+ await input.program.parseAsync(built.argv, { from: 'user' });
27
+ return null;
28
+ }
29
+ catch (error) {
30
+ return error;
31
+ }
32
+ });
33
+ const settled = await raceWithTimeout(running, timeoutMs);
34
+ const stderr = sink.stderr.join('');
35
+ if (settled.timedOut) {
36
+ // The abandoned command keeps its own sink through {@link installStdioCapture}, so
37
+ // whatever it writes later cannot land in the next request's output.
38
+ return respond.failure({ message: `Request timed out after ${timeoutMs}ms.`, code: 'session_request_timeout' }, 1, stderr);
39
+ }
40
+ // Commands report failure through `process.exitCode`; an untouched code means success.
41
+ const exitCode = typeof process.exitCode === 'number' ? process.exitCode : 0;
42
+ process.exitCode = 0;
43
+ const stdout = sink.stdout.join('');
44
+ if (settled.error) {
45
+ return fromThrownError(respond, settled.error, stdout, stderr);
46
+ }
47
+ if (exitCode !== 0) {
48
+ return respond.failure(errorDetailFrom(stdout, stderr), exitCode, stderr);
49
+ }
50
+ return respond.success(stdout, stderr);
51
+ };
52
+ /** `--help` and `--version` reach us as a zero-exit Commander throw; both are legitimate answers. */
53
+ const fromThrownError = (respond, error, stdout, stderr) => {
54
+ if (!(error instanceof CommanderError)) {
55
+ return respond.failure({ message: formatError(error), code: 'session_command_failed' }, 1, stderr);
56
+ }
57
+ if (error.exitCode === 0) {
58
+ return respond.success(stdout, stderr);
59
+ }
60
+ return respond.failure({ message: error.message, code: 'session_invalid_request' }, error.exitCode || 2, stderr);
61
+ };
62
+ /**
63
+ * Decode what the command wrote to stdout.
64
+ *
65
+ * One JSON document decodes to itself, several decode to an array (`stream: true`), and
66
+ * anything that is not JSON is handed back verbatim (`raw: true`) rather than guessed at.
67
+ */
68
+ const decodeStdout = (stdout) => {
69
+ const lines = stdout.split('\n').filter((line) => line.trim() !== '');
70
+ if (lines.length === 0) {
71
+ return { result: null };
72
+ }
73
+ const documents = [];
74
+ for (const line of lines) {
75
+ try {
76
+ documents.push(JSON.parse(line));
77
+ }
78
+ catch {
79
+ return { result: stdout, raw: true };
80
+ }
81
+ }
82
+ return documents.length === 1 ? { result: documents[0] } : { result: documents, stream: true };
83
+ };
84
+ /**
85
+ * Recover the machine-readable failure a command already produced.
86
+ *
87
+ * A watcher-side failure arrives as the standard `ok: false` envelope on stdout; a local
88
+ * failure (bad flag combination, unresolvable watcher) only wrote prose to stderr.
89
+ */
90
+ const errorDetailFrom = (stdout, stderr) => {
91
+ const document = decodeStdout(stdout).result;
92
+ if (document && typeof document === 'object' && document.ok === false) {
93
+ const detail = document.error;
94
+ if (detail?.message) {
95
+ return detail;
96
+ }
97
+ }
98
+ const message = stderr.trim().split('\n').filter(Boolean).at(-1);
99
+ return { message: message ?? 'Command failed.', code: 'session_command_failed' };
100
+ };
101
+ /**
102
+ * Bind the fields both response arms share — the correlation id and the elapsed time — so the
103
+ * seven exit paths above only name what actually differs between them.
104
+ */
105
+ const createResponder = (request, startedAt) => {
106
+ const id = request.id === undefined ? {} : { id: request.id };
107
+ const trailer = (stderr) => ({ durationMs: Date.now() - startedAt, ...(stderr === '' ? {} : { stderr }) });
108
+ return {
109
+ success: (stdout, stderr) => ({ ...id, ok: true, ...decodeStdout(stdout), ...trailer(stderr) }),
110
+ failure: (error, exitCode, stderr = '') => ({ ...id, ok: false, error, exitCode, ...trailer(stderr) }),
111
+ };
112
+ };
113
+ const resolveTimeoutMs = (timeout, fallbackMs) => {
114
+ if (timeout == null) {
115
+ return fallbackMs;
116
+ }
117
+ if (typeof timeout === 'number') {
118
+ return timeout;
119
+ }
120
+ return parseDurationMs(timeout, 'ms');
121
+ };
122
+ const raceWithTimeout = async (running, timeoutMs) => {
123
+ if (timeoutMs <= 0) {
124
+ return { timedOut: false, error: await running };
125
+ }
126
+ let timer;
127
+ const watchdog = new Promise((resolve) => {
128
+ timer = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
129
+ });
130
+ try {
131
+ return await Promise.race([running.then((error) => ({ timedOut: false, error })), watchdog]);
132
+ }
133
+ finally {
134
+ clearTimeout(timer);
135
+ }
136
+ };
137
+ //# sourceMappingURL=sessionDispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionDispatch.js","sourceRoot":"","sources":["../../src/session/sessionDispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /** Text a single request wrote to each stream. */
2
+ export type CapturedStdio = {
3
+ stdout: string[];
4
+ stderr: string[];
5
+ };
6
+ /** Handle returned by {@link installStdioCapture}. */
7
+ export type StdioCapture = {
8
+ /**
9
+ * Run `body` with its stdout/stderr redirected into `sink`.
10
+ *
11
+ * The sink travels with the async context, not with wall-clock time: a command that
12
+ * outlives its watchdog keeps writing into its own (already abandoned) sink instead of
13
+ * corrupting whichever request is running by then.
14
+ */
15
+ run: <T>(sink: CapturedStdio, body: () => Promise<T>) => Promise<T>;
16
+ /** Write straight to the real stdout, past the capture. Used for the JSONL responses themselves. */
17
+ writeStdout: (text: string) => void;
18
+ /** Restore the original stream writers. */
19
+ restore: () => void;
20
+ };
21
+ /**
22
+ * Redirect every stdout write made inside a request into that request's own buffer.
23
+ *
24
+ * The session's contract is that stdout carries nothing but its JSONL responses, and the
25
+ * ~200 commands it dispatches all reach stdout through their own `Output` closures — plus a
26
+ * handful of direct `process.stdout.write` calls. Patching the stream is what makes that
27
+ * contract hold for all of them at once, including plugin commands this file has never seen.
28
+ *
29
+ * stderr is captured *and* mirrored: the response carries it so a host can report a failure
30
+ * without a second channel, and a human tailing the session's stderr still sees it live.
31
+ */
32
+ export declare const installStdioCapture: () => StdioCapture;
33
+ //# sourceMappingURL=stdioCapture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdioCapture.d.ts","sourceRoot":"","sources":["../../src/session/stdioCapture.ts"],"names":[],"mappings":"AAEA,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,MAAM,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED,sDAAsD;AACtD,MAAM,MAAM,YAAY,GAAG;IAC1B;;;;;;OAMG;IACH,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IACnE,oGAAoG;IACpG,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB,CAAA;AAOD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,QAAO,YAkBtC,CAAA"}
@@ -0,0 +1,56 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ /**
3
+ * Redirect every stdout write made inside a request into that request's own buffer.
4
+ *
5
+ * The session's contract is that stdout carries nothing but its JSONL responses, and the
6
+ * ~200 commands it dispatches all reach stdout through their own `Output` closures — plus a
7
+ * handful of direct `process.stdout.write` calls. Patching the stream is what makes that
8
+ * contract hold for all of them at once, including plugin commands this file has never seen.
9
+ *
10
+ * stderr is captured *and* mirrored: the response carries it so a host can report a failure
11
+ * without a second channel, and a human tailing the session's stderr still sees it live.
12
+ */
13
+ export const installStdioCapture = () => {
14
+ const storage = new AsyncLocalStorage();
15
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
16
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
17
+ process.stdout.write = createCapturingWrite(storage, originalStdoutWrite, (sink) => sink.stdout, false);
18
+ process.stderr.write = createCapturingWrite(storage, originalStderrWrite, (sink) => sink.stderr, true);
19
+ return {
20
+ run: (sink, body) => storage.run(sink, body),
21
+ writeStdout: (text) => {
22
+ originalStdoutWrite(text);
23
+ },
24
+ restore: () => {
25
+ process.stdout.write = originalStdoutWrite;
26
+ process.stderr.write = originalStderrWrite;
27
+ },
28
+ };
29
+ };
30
+ const createCapturingWrite = (storage, original, select, mirror) => {
31
+ const forward = original;
32
+ return ((chunk, encoding, callback) => {
33
+ const sink = storage.getStore();
34
+ if (!sink) {
35
+ return forward(chunk, encoding, callback);
36
+ }
37
+ select(sink).push(decodeChunk(chunk, encoding));
38
+ if (mirror) {
39
+ forward(chunk, encoding);
40
+ }
41
+ const done = typeof encoding === 'function' ? encoding : callback;
42
+ if (typeof done === 'function') {
43
+ ;
44
+ done();
45
+ }
46
+ return true;
47
+ });
48
+ };
49
+ const decodeChunk = (chunk, encoding) => {
50
+ if (typeof chunk === 'string') {
51
+ return chunk;
52
+ }
53
+ const bufferEncoding = typeof encoding === 'string' ? encoding : 'utf8';
54
+ return Buffer.from(chunk).toString(bufferEncoding);
55
+ };
56
+ //# sourceMappingURL=stdioCapture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdioCapture.js","sourceRoot":"","sources":["../../src/session/stdioCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AA6BpD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAiB,EAAE;IACrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAiB,CAAA;IACtD,MAAM,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAgB,CAAA;IACpF,MAAM,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAgB,CAAA;IAEpF,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IACvG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAEtG,OAAO;QACN,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;QAC5C,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;YACrB,mBAAmB,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC;QACD,OAAO,EAAE,GAAG,EAAE;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,mBAAmB,CAAA;YAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,mBAAmB,CAAA;QAC3C,CAAC;KACD,CAAA;AACF,CAAC,CAAA;AAED,MAAM,oBAAoB,GAAG,CAC5B,OAAyC,EACzC,QAAqB,EACrB,MAAyC,EACzC,MAAe,EACD,EAAE;IAChB,MAAM,OAAO,GAAG,QAAsB,CAAA;IAEtC,OAAO,CAAC,CAAC,KAA0B,EAAE,QAAkB,EAAE,QAAkB,EAAW,EAAE;QACvF,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAA;QAC/B,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,OAAO,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC1C,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC/C,IAAI,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACzB,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAA;QACjE,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;YAChC,CAAC;YAAC,IAAmB,EAAE,CAAA;QACxB,CAAC;QACD,OAAO,IAAI,CAAA;IACZ,CAAC,CAAgB,CAAA;AAClB,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,KAA0B,EAAE,QAAiB,EAAU,EAAE;IAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAA;IACb,CAAC;IACD,MAAM,cAAc,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAE,QAA2B,CAAC,CAAC,CAAC,MAAM,CAAA;IAC3F,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;AACnD,CAAC,CAAA"}
@@ -155,6 +155,34 @@ For deeper command lists, use [INSPECT.md](./reference/INSPECT.md). For screensh
155
155
 
156
156
  ---
157
157
 
158
+ ## Session Transport (Many Commands, One Process)
159
+
160
+ Use this when a harness drives a page through many sequential steps. One-shot `argus` pays Node
161
+ startup plus watcher discovery (~100-200ms) per command; `argus session` pays it once and then
162
+ serves JSONL requests on stdin.
163
+
164
+ ```bash
165
+ argus session app
166
+ ```
167
+
168
+ ```json
169
+ {"id": 1, "cmd": "eval", "args": {"expression": "location.href"}}
170
+ {"id": 2, "cmd": "click", "args": {"selector": "button.start"}}
171
+ {"id": 3, "cmd": "quit"}
172
+ ```
173
+
174
+ Responses are one JSON line each, correlated by `id`, carrying the same payload the matching
175
+ `--json` command prints: `{"id": 1, "ok": true, "result": {…}, "durationMs": 8}`. Failures answer
176
+ `ok: false` and the session stays alive. stdout is JSONL only; human output goes to stderr.
177
+
178
+ Daemons (`start`, `chrome start`, `watcher start`), stream tails (`logs tail`, `net tail`,
179
+ `net sse`), and anything reading `--stdin` are refused — run those as their own process.
180
+
181
+ Read [SESSION.md](./reference/SESSION.md) for the full request/response schema, timeout and
182
+ watcher-loss semantics, and a host sketch.
183
+
184
+ ---
185
+
158
186
  ## CDP Quick Start
159
187
 
160
188
  Use CDP for local apps and clean repros where a temp/debuggable browser is acceptable.
@@ -226,3 +254,4 @@ Never treat whole-export row indexes as physical sheet rows: `exportRow` is only
226
254
  - [INJECT.md](./reference/INJECT.md) — Script injection on watcher attach/navigation.
227
255
  - [DIALOG.md](./reference/DIALOG.md) — Browser dialog status and handling.
228
256
  - [PLUGINS.md](./reference/PLUGINS.md) — CLI plugin loading and Google Sheets plugin.
257
+ - [SESSION.md](./reference/SESSION.md) — Long-lived JSONL session transport for automation harnesses.