@astrosheep/square 0.4.7 → 0.4.9
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/dist/card-format.d.ts +2 -0
- package/dist/card-format.js +2 -0
- package/dist/claude-mcp-context.d.ts +0 -1
- package/dist/claude-mcp-context.js +0 -10
- package/dist/claude-mod.d.ts +2 -0
- package/dist/claude-mod.js +211 -0
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +1 -12
- package/dist/cli/express-presentation.js +8 -35
- package/dist/cli/json.js +1 -1
- package/dist/cli/observation-commands.d.ts +2 -3
- package/dist/cli/observation-commands.js +21 -59
- package/dist/cli/registry.js +3 -5
- package/dist/cli/square-commands.d.ts +1 -2
- package/dist/cli/square-commands.js +7 -17
- package/dist/decisions.js +15 -20
- package/dist/harness-claude.js +3 -3
- package/dist/help.js +6 -8
- package/dist/mcp.js +1 -1
- package/dist/neutral.d.ts +15 -1
- package/dist/notifications.js +1 -1
- package/dist/operation-presentation.d.ts +17 -15
- package/dist/operation-presentation.js +63 -37
- package/dist/packages/agent-delivery/src/index.d.ts +4 -9
- package/dist/packages/agent-delivery/src/index.js +0 -9
- package/dist/packages/agent-delivery/src/local-endpoint.d.ts +2 -12
- package/dist/packages/agent-delivery/src/local-endpoint.js +4 -48
- package/dist/packages/agent-delivery/src/pi-client.js +0 -2
- package/dist/packages/agent-delivery/src/types.d.ts +3 -37
- package/dist/plugins/paseo/shared/card-format.d.ts +6 -0
- package/dist/plugins/paseo/shared/card-format.js +38 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +3 -1
- package/dist/plugins/paseo/shared/card-presentation.js +3 -1
- package/dist/presentation.d.ts +2 -20
- package/dist/presentation.js +11 -46
- package/dist/sqlite-square.js +5 -6
- package/dist/square-facade.d.ts +4 -13
- package/dist/square-posting.js +1 -1
- package/dist/square-tools.d.ts +0 -11
- package/dist/square-tools.js +34 -120
- package/dist/square-values.d.ts +1 -1
- package/dist/square-values.js +1 -1
- package/dist/time.d.ts +1 -0
- package/dist/time.js +10 -0
- package/dist/views.d.ts +1 -1
- package/dist/views.js +2 -2
- package/dist/wake-transports.d.ts +1 -1
- package/dist/wake-transports.js +1 -1
- package/guides/catch-cold-path-optimization.md +1 -1
- package/guides/claude-native.md +37 -7
- package/guides/delivery-primitives.md +2 -1
- package/guides/mcp.md +3 -3
- package/guides/pi-native-delivery.md +10 -2
- package/guides/pi-tools.md +1 -3
- package/guides/row-storage-design.md +1 -1
- package/guides/square-api.md +6 -0
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +3 -2
- package/plugins/claude/README.md +9 -0
- package/plugins/claude/hooks/hooks.json +1 -9
- package/plugins/claude/hooks/register.js +178 -0
- package/plugins/claude/skills/square/SKILL.md +4 -4
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/paseo/OVERVIEW.md +1 -1
- package/plugins/paseo/README.md +20 -23
- package/plugins/paseo/client/markdown.tsx +88 -95
- package/plugins/paseo/client/palette.ts +37 -12
- package/plugins/paseo/client/square-message.tsx +52 -76
- package/plugins/paseo/client/transform.ts +4 -6
- package/plugins/paseo/shared/card-format.ts +37 -0
- package/plugins/paseo/shared/card-presentation.ts +6 -2
- package/plugins/paseo/shared/message.ts +1 -6
- package/plugins/pi/square-pi.js +69 -50
- package/skills/square/SKILL.md +4 -4
- package/dist/claude-boundary-state.d.ts +0 -20
- package/dist/claude-boundary-state.js +0 -84
- package/dist/claude-hook.d.ts +0 -2
- package/dist/claude-hook.js +0 -232
- package/dist/packages/agent-delivery/src/claude-auth.d.ts +0 -13
- package/dist/packages/agent-delivery/src/claude-auth.js +0 -75
- package/dist/packages/agent-delivery/src/claude-native.d.ts +0 -30
- package/dist/packages/agent-delivery/src/claude-native.js +0 -75
- package/dist/packages/agent-delivery/src/claude.d.ts +0 -5
- package/dist/packages/agent-delivery/src/claude.js +0 -66
|
@@ -10,6 +10,5 @@ export declare function beginClaudeMcpCall(input: ClaudeMcpCall, env?: NodeJS.Pr
|
|
|
10
10
|
token: string;
|
|
11
11
|
}>;
|
|
12
12
|
export declare function endClaudeMcpCall(toolUseId: string, token: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
13
|
-
export declare function endClaudeMcpCallForSession(toolUseId: string, sessionId: string, env?: NodeJS.ProcessEnv, agentId?: string, signal?: AbortSignal): Promise<void>;
|
|
14
13
|
export declare function readClaudeMcpCall(toolUseId: unknown, env?: NodeJS.ProcessEnv): Promise<ClaudeMcpCall>;
|
|
15
14
|
export {};
|
|
@@ -71,16 +71,6 @@ export async function endClaudeMcpCall(toolUseId, token, env = process.env) {
|
|
|
71
71
|
await fs.unlink(file);
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
export async function endClaudeMcpCallForSession(toolUseId, sessionId, env = process.env, agentId, signal) {
|
|
75
|
-
if (!text.safeParse(toolUseId).success || !text.safeParse(sessionId).success)
|
|
76
|
-
throw unavailable();
|
|
77
|
-
await mutate(env, async (root) => {
|
|
78
|
-
const file = callFile(root, toolUseId);
|
|
79
|
-
const stored = await readRecord(file);
|
|
80
|
-
if (stored?.toolUseId === toolUseId && stored.sessionId === sessionId && stored.agentId === agentId)
|
|
81
|
-
await fs.unlink(file);
|
|
82
|
-
}, signal);
|
|
83
|
-
}
|
|
84
74
|
export async function readClaudeMcpCall(toolUseId, env = process.env) {
|
|
85
75
|
if (!text.safeParse(toolUseId).success)
|
|
86
76
|
throw unavailable();
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { once } from 'node:events';
|
|
4
|
+
import { z } from 'zod/v4';
|
|
5
|
+
import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
|
|
6
|
+
import { retainDeliveryLedger } from './delivery-lifetime.js';
|
|
7
|
+
import { harnessOwner } from './owner.js';
|
|
8
|
+
import { attachDeliverySession, detachDeliverySession } from './session-lifecycle.js';
|
|
9
|
+
import { beginClaudeMcpCall, endClaudeMcpCall } from './claude-mcp-context.js';
|
|
10
|
+
import { renderDeliveryReminder } from './delivery-frame.js';
|
|
11
|
+
const routeKind = 'claude-mod';
|
|
12
|
+
const nativeId = z.string().min(1).max(4096).regex(/^[^\x00-\x1f\x7f]+$/u);
|
|
13
|
+
const coordinate = z.object({ agentId: nativeId.optional() });
|
|
14
|
+
const request = z.discriminatedUnion('op', [
|
|
15
|
+
coordinate.extend({ op: z.literal('state'), mode: z.enum(['busy', 'idle', 'stopped', 'ended']) }),
|
|
16
|
+
z.object({ op: z.literal('guard'), token: z.string().uuid() }),
|
|
17
|
+
z.object({ op: z.literal('settle'), token: z.string().uuid(), kind: z.enum(['append', 'submit', 'failed', 'unknown', 'released']),
|
|
18
|
+
text: z.string().optional(), uuid: nativeId.optional() }),
|
|
19
|
+
coordinate.extend({ op: z.literal('mcp-begin'), toolUseId: nativeId, cwd: z.string().min(1) }),
|
|
20
|
+
z.object({ op: z.literal('mcp-end'), toolUseId: nativeId, token: nativeId }),
|
|
21
|
+
z.object({ op: z.literal('close') }),
|
|
22
|
+
]);
|
|
23
|
+
/** One mod-owned helper. Only this process touches Node/SQLite; no producer socket or inbox. */
|
|
24
|
+
export async function runClaudeMod(input, env = process.env, signal, output = line => new Promise((resolve, reject) => {
|
|
25
|
+
process.stdout.write(line, error => error ? reject(error) : resolve());
|
|
26
|
+
})) {
|
|
27
|
+
const { sessionId } = z.object({ sessionId: nativeId }).parse(JSON.parse(input));
|
|
28
|
+
const stop = new AbortController();
|
|
29
|
+
const abort = () => stop.abort();
|
|
30
|
+
const lifetimeSignal = signal ? AbortSignal.any([signal, stop.signal]) : stop.signal;
|
|
31
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal: AbortSignal.any([lifetimeSignal, AbortSignal.timeout(5000)]) });
|
|
32
|
+
process.once('SIGTERM', abort);
|
|
33
|
+
process.once('SIGINT', abort);
|
|
34
|
+
const lifetime = retainDeliveryLedger(ledger);
|
|
35
|
+
const receivers = new Map();
|
|
36
|
+
const offers = new Map();
|
|
37
|
+
const secret = `/${randomBytes(32).toString('hex')}`;
|
|
38
|
+
let revision = 0;
|
|
39
|
+
const ownerFor = (agentId) => harnessOwner({ channel: 'claude-code', session: sessionId, ...(agentId === undefined ? {} : { agent: agentId }) });
|
|
40
|
+
async function state(agentId, mode, signal = lifetimeSignal) {
|
|
41
|
+
const key = agentId ?? '';
|
|
42
|
+
let receiver = receivers.get(key);
|
|
43
|
+
if ((!receiver || receiver.mode === 'ended') && (mode === 'busy' || mode === 'idle')) {
|
|
44
|
+
const owner = ownerFor(agentId);
|
|
45
|
+
const attached = await attachDeliverySession(owner, env, [{ kind: routeKind, address: { sessionId, ...(agentId === undefined ? {} : { agentId }) } }], signal);
|
|
46
|
+
if (!attached)
|
|
47
|
+
return false;
|
|
48
|
+
receiver = { agentId, owner, generation: attached.generation, mode, attempted: new Set() };
|
|
49
|
+
receivers.set(key, receiver);
|
|
50
|
+
}
|
|
51
|
+
if (!receiver)
|
|
52
|
+
return false;
|
|
53
|
+
if (mode === 'busy' && receiver.mode === 'stopped')
|
|
54
|
+
receiver.attempted.clear();
|
|
55
|
+
receiver.mode = mode;
|
|
56
|
+
revision++;
|
|
57
|
+
if (mode === 'stopped')
|
|
58
|
+
await ledger.cancel({ owner: receiver.owner, generation: receiver.generation }, signal);
|
|
59
|
+
if (mode === 'ended')
|
|
60
|
+
await detachDeliverySession(receiver.owner, receiver.generation, env, signal);
|
|
61
|
+
if (mode === 'stopped' || mode === 'ended')
|
|
62
|
+
for (const offer of offers.values()) {
|
|
63
|
+
if (offer.receiver === receiver)
|
|
64
|
+
offer.finish(offer.invoked ? 'unknown' : 'not-capable');
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
async function rpc(data) {
|
|
69
|
+
switch (data.op) {
|
|
70
|
+
case 'state': return { ok: await state(data.agentId, data.mode) };
|
|
71
|
+
case 'guard': {
|
|
72
|
+
const offer = offers.get(data.token);
|
|
73
|
+
if (!offer || !['busy', 'idle'].includes(offer.receiver.mode) || !await offer.guard())
|
|
74
|
+
return { ok: false };
|
|
75
|
+
// Crossing this fence may invoke a native effect. Cancellation thereafter is uncertain.
|
|
76
|
+
offer.invoked = true;
|
|
77
|
+
return { ok: true };
|
|
78
|
+
}
|
|
79
|
+
case 'settle': {
|
|
80
|
+
const offer = offers.get(data.token);
|
|
81
|
+
if (!offer)
|
|
82
|
+
return { ok: false };
|
|
83
|
+
const exact = data.text === offer.text;
|
|
84
|
+
const outcome = data.kind === 'append' ? exact && data.uuid ? 'presented' : 'unknown'
|
|
85
|
+
: data.kind === 'submit' ? exact ? 'presented' : 'accepted'
|
|
86
|
+
: data.kind === 'released' ? 'not-capable' : data.kind;
|
|
87
|
+
offer.finish(outcome);
|
|
88
|
+
return { ok: true };
|
|
89
|
+
}
|
|
90
|
+
case 'mcp-begin': {
|
|
91
|
+
const receiver = receivers.get(data.agentId ?? '');
|
|
92
|
+
if (!receiver || receiver.mode !== 'busy') {
|
|
93
|
+
// Some native child starts are nested under another mod's event. The
|
|
94
|
+
// actual tool event is still authoritative, never its model arguments.
|
|
95
|
+
if (receiver?.mode === 'stopped' || !await state(data.agentId, 'busy'))
|
|
96
|
+
throw new Error('Square MCP call identity unavailable');
|
|
97
|
+
}
|
|
98
|
+
return beginClaudeMcpCall({ toolUseId: data.toolUseId, sessionId, cwd: data.cwd, agentId: data.agentId }, env, lifetimeSignal);
|
|
99
|
+
}
|
|
100
|
+
case 'mcp-end':
|
|
101
|
+
await endClaudeMcpCall(data.toolUseId, data.token, env);
|
|
102
|
+
return { ok: true };
|
|
103
|
+
case 'close':
|
|
104
|
+
// The mod may kill its process stream as soon as this RPC returns.
|
|
105
|
+
// Commit detach before acknowledging, not only in process teardown.
|
|
106
|
+
for (const receiver of receivers.values())
|
|
107
|
+
await state(receiver.agentId, 'ended');
|
|
108
|
+
stop.abort();
|
|
109
|
+
return { ok: true };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const server = createServer(async (req, res) => {
|
|
113
|
+
if (req.method !== 'POST' || req.url !== secret) {
|
|
114
|
+
res.writeHead(404).end();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
let body = '';
|
|
119
|
+
for await (const chunk of req) {
|
|
120
|
+
body += chunk.toString();
|
|
121
|
+
if (body.length > 1_048_576)
|
|
122
|
+
throw new Error('Request too large');
|
|
123
|
+
}
|
|
124
|
+
const result = await rpc(request.parse(JSON.parse(body)));
|
|
125
|
+
res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(result));
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
res.writeHead(400, { 'content-type': 'application/json' }).end(JSON.stringify({ error: 'Square Claude mod request failed' }));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
let observer;
|
|
132
|
+
try {
|
|
133
|
+
server.listen(0, '127.0.0.1');
|
|
134
|
+
await once(server, 'listening', { signal: lifetimeSignal });
|
|
135
|
+
const address = server.address();
|
|
136
|
+
if (!address || typeof address === 'string')
|
|
137
|
+
throw new Error('Claude helper address unavailable');
|
|
138
|
+
if (!await state(undefined, 'idle', AbortSignal.any([lifetimeSignal, AbortSignal.timeout(5000)])))
|
|
139
|
+
throw new Error('Claude helper attachment unavailable');
|
|
140
|
+
observer = await ledger.observe(ownerFor());
|
|
141
|
+
await output(`${JSON.stringify({ type: 'ready', url: `http://127.0.0.1:${address.port}${secret}` })}\n`);
|
|
142
|
+
while (!lifetimeSignal.aborted) {
|
|
143
|
+
let version = await observer.read(lifetimeSignal);
|
|
144
|
+
const previousRevision = revision;
|
|
145
|
+
for (const receiver of receivers.values()) {
|
|
146
|
+
if (!['busy', 'idle'].includes(receiver.mode))
|
|
147
|
+
continue;
|
|
148
|
+
const current = await ledger.session(receiver.owner, lifetimeSignal);
|
|
149
|
+
if (current?.state !== 'attached' || current.generation !== receiver.generation) {
|
|
150
|
+
if (receiver.agentId === undefined)
|
|
151
|
+
return '';
|
|
152
|
+
receiver.mode = 'ended';
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const held = new Set((await ledger.handoffs(receiver.owner, { generation: receiver.generation }, lifetimeSignal))
|
|
156
|
+
.filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
|
|
157
|
+
for (const card of await ledger.pending(receiver.owner, { generation: receiver.generation }, lifetimeSignal)) {
|
|
158
|
+
if (lifetimeSignal.aborted || !['busy', 'idle'].includes(receiver.mode))
|
|
159
|
+
break;
|
|
160
|
+
if (held.has(card.seq) || receiver.attempted.has(card.key))
|
|
161
|
+
continue;
|
|
162
|
+
// An idle child cannot be woken with the main session's prompt.submit.
|
|
163
|
+
if (receiver.agentId !== undefined && receiver.mode !== 'busy')
|
|
164
|
+
continue;
|
|
165
|
+
receiver.attempted.add(card.key);
|
|
166
|
+
const transport = {
|
|
167
|
+
kind: routeKind,
|
|
168
|
+
async send(deliveryCard, _address, context) {
|
|
169
|
+
const text = renderDeliveryReminder([`[square-delivery:${context.token}]\n${deliveryCard.text}\n[/square-delivery:${context.token}]`], { tagged: false });
|
|
170
|
+
if (!await context.prepare({ token: context.token, messageId: context.token, payloadHash: createHash('sha256').update(text).digest('hex') })
|
|
171
|
+
|| !await context.beforeSend() || !['busy', 'idle'].includes(receiver.mode))
|
|
172
|
+
return 'not-capable';
|
|
173
|
+
const completion = Promise.withResolvers();
|
|
174
|
+
offers.set(context.token, { receiver, text, invoked: false, guard: context.beforeSend, finish: completion.resolve });
|
|
175
|
+
try {
|
|
176
|
+
await output(`${JSON.stringify({ type: 'delivery', token: context.token, agentId: receiver.agentId, text, display: deliveryCard.text })}\n`);
|
|
177
|
+
return await completion.promise;
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
offers.delete(context.token);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
lifetime.track(await ledger.dispatchHandoff(receiver.owner, card.key, transport, { generation: receiver.generation, timeoutMs: 5000, signal: lifetimeSignal }));
|
|
185
|
+
// Settlement changes are our own; drain them rather than busy-retrying.
|
|
186
|
+
version = await observer.read(lifetimeSignal);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (!lifetimeSignal.aborted && previousRevision === revision)
|
|
190
|
+
await observer.changed(version, 1000, lifetimeSignal);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (!lifetimeSignal.aborted)
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
stop.abort();
|
|
199
|
+
for (const offer of offers.values())
|
|
200
|
+
offer.finish(offer.invoked ? 'unknown' : 'not-capable');
|
|
201
|
+
server.closeAllConnections();
|
|
202
|
+
await new Promise(resolve => server.close(() => resolve()));
|
|
203
|
+
observer?.close();
|
|
204
|
+
for (const receiver of receivers.values())
|
|
205
|
+
await detachDeliverySession(receiver.owner, receiver.generation, env, AbortSignal.timeout(1500)).catch(() => undefined);
|
|
206
|
+
await lifetime.close();
|
|
207
|
+
process.off('SIGTERM', abort);
|
|
208
|
+
process.off('SIGINT', abort);
|
|
209
|
+
}
|
|
210
|
+
return '';
|
|
211
|
+
}
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare function resolveBody(arg: string): Promise<string>;
|
|
|
22
22
|
export declare function fail(message: string, exitCode?: number): never;
|
|
23
23
|
export declare function usage(command: string): never;
|
|
24
24
|
export declare function requireValue(args: string[], index: number, flag: string): string;
|
|
25
|
-
export
|
|
25
|
+
export { parseDurationMs } from '../time.js';
|
|
26
26
|
export declare function parsePositiveInteger(value: string, flag: string): number;
|
|
27
27
|
export declare function parseBoundedLimit(value: string | undefined, flag: string, max: number, retry: string): number;
|
|
28
28
|
export declare function parseNonNegativeInteger(value: string, flag: string): number;
|
package/dist/cli/context.js
CHANGED
|
@@ -44,17 +44,7 @@ export function requireValue(args, index, flag) {
|
|
|
44
44
|
fail(`✕ missing value for ${flag}`);
|
|
45
45
|
return value;
|
|
46
46
|
}
|
|
47
|
-
export
|
|
48
|
-
const match = value.match(/^([1-9]\d*)(ms|s|m|h)$/);
|
|
49
|
-
if (!match)
|
|
50
|
-
fail(`✕ invalid ${flag}: expected a positive duration with unit ms, s, m, or h (for example 500ms, 30s, 3m, 1h)`);
|
|
51
|
-
const amount = Number.parseInt(match[1], 10);
|
|
52
|
-
const multiplier = { ms: 1, s: 1000, m: 60000, h: 3600000 };
|
|
53
|
-
const milliseconds = amount * multiplier[match[2]];
|
|
54
|
-
if (!Number.isSafeInteger(milliseconds))
|
|
55
|
-
fail(`✕ invalid ${flag}: duration is too large`);
|
|
56
|
-
return milliseconds;
|
|
57
|
-
}
|
|
47
|
+
export { parseDurationMs } from '../time.js';
|
|
58
48
|
export function parsePositiveInteger(value, flag) {
|
|
59
49
|
if (!/^[1-9]\d*$/.test(value))
|
|
60
50
|
fail(`✕ invalid ${flag}: expected a positive integer`);
|
|
@@ -107,7 +97,6 @@ const LOCATION_REQUIRED_COMMANDS = new Set([
|
|
|
107
97
|
'listen',
|
|
108
98
|
'ignore',
|
|
109
99
|
'unlisten',
|
|
110
|
-
'listening',
|
|
111
100
|
'done',
|
|
112
101
|
'history',
|
|
113
102
|
'status',
|
|
@@ -3,9 +3,8 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { isSquareError } from '../model.js';
|
|
5
5
|
import { selectOwnSeat, formatActivityId, parseActivityId } from '../square-api.js';
|
|
6
|
-
import { formatRefusal,
|
|
7
|
-
import { renderExpression, renderFailureLines } from '../operation-presentation.js';
|
|
8
|
-
import { formatDuration } from '../time.js';
|
|
6
|
+
import { formatRefusal, renderActivityUncertain, renderExpressWaiting, participantCommandPrefix, commandPrefix, quoteShell, withPathOutput, } from '../presentation.js';
|
|
7
|
+
import { isExpressGate, renderExpression, renderFailureLines } from '../operation-presentation.js';
|
|
9
8
|
import { jsonResult } from './json.js';
|
|
10
9
|
export { renderExpression } from '../operation-presentation.js';
|
|
11
10
|
export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
|
|
@@ -101,36 +100,10 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
101
100
|
exitCode(1);
|
|
102
101
|
return;
|
|
103
102
|
}
|
|
104
|
-
if (isSquareError(error)) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}));
|
|
109
|
-
exitCode(1);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (error.code === 'capped') {
|
|
113
|
-
process.stdout.write(renderActivityLimit({ ...output, count: fresh?.ownActivityCount,
|
|
114
|
-
...(fresh?.hardCap == null ? {} : { hardCap: fresh.hardCap }),
|
|
115
|
-
}));
|
|
116
|
-
exitCode(1);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (error.code === 'throttled') {
|
|
120
|
-
process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand: retryInput,
|
|
121
|
-
delayMs: error.facts?.retryAfterMs ?? 1,
|
|
122
|
-
}));
|
|
123
|
-
exitCode(1);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (error.code === 'bell_quota') {
|
|
127
|
-
process.stdout.write(withPathOutput(squarePath, [
|
|
128
|
-
'✕ the bell stays quiet for now', ` · you can ring it again in ${formatDuration(error.facts?.retryAfterMs ?? 1)}`,
|
|
129
|
-
`· draft kept: ${draftPath}`, ' · retry when the bell is ready:', retryInput,
|
|
130
|
-
].join('\n'), output));
|
|
131
|
-
exitCode(1);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
103
|
+
if (isSquareError(error) && isExpressGate(error.code)) {
|
|
104
|
+
process.stdout.write(withPathOutput(squarePath, renderFailureLines({ code: error.code, message: error.message, facts: error.facts }, { squarePath, name, owner: opts.recoveryOwner, draftPath, retry: retryInput }).join('\n'), output));
|
|
105
|
+
exitCode(1);
|
|
106
|
+
return;
|
|
134
107
|
}
|
|
135
108
|
const facts = isSquareError(error) ? error.facts : undefined;
|
|
136
109
|
const reason = facts?.reason;
|
|
@@ -142,10 +115,10 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
142
115
|
exitCode(2);
|
|
143
116
|
return;
|
|
144
117
|
}
|
|
145
|
-
const repair = reason === 'bell_mention_conflict'
|
|
118
|
+
const repair = reason === 'bell_mention_conflict';
|
|
146
119
|
if (isSquareError(error)) {
|
|
147
120
|
if (repair) {
|
|
148
|
-
lines.push(
|
|
121
|
+
lines.push(' · ring the bell without explicit mentions:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [] })}${opts.bodyProvided === false ? '' : ` < ${quoteShell(draftPath)}`}`);
|
|
149
122
|
}
|
|
150
123
|
else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
|
|
151
124
|
if (opts.mentions?.length)
|
package/dist/cli/json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const JSON_COMMANDS = new Set([
|
|
2
|
-
'join', 'express', 'perceive', 'history', 'listen', 'ignore', 'unlisten',
|
|
2
|
+
'join', 'express', 'perceive', 'history', 'listen', 'ignore', 'unlisten',
|
|
3
3
|
'done', 'status', 'inbox',
|
|
4
4
|
]);
|
|
5
5
|
/** Serialize the SDK receipt, not its optional presentation or live participant handle. */
|
|
@@ -2,7 +2,7 @@ import type { PerceiveOptions, DiscoveryOptions, HistoryQuery, StreamStart } fro
|
|
|
2
2
|
import { type CommandSpec } from './context.js';
|
|
3
3
|
interface HistoryCommandOptions {
|
|
4
4
|
query: HistoryQuery;
|
|
5
|
-
|
|
5
|
+
full: boolean;
|
|
6
6
|
format?: string[];
|
|
7
7
|
continuationArgs: string[];
|
|
8
8
|
}
|
|
@@ -21,8 +21,7 @@ interface InboxIntent {
|
|
|
21
21
|
limit: number;
|
|
22
22
|
}
|
|
23
23
|
export declare const inboxCommand: CommandSpec<InboxIntent, string>;
|
|
24
|
-
export declare const
|
|
25
|
-
export declare const claudeWatchCommand: CommandSpec<undefined, string>;
|
|
24
|
+
export declare const claudeModCommand: CommandSpec<undefined, string>;
|
|
26
25
|
export declare const codexHookCommand: CommandSpec<undefined, string>;
|
|
27
26
|
export declare const codexWatchCommand: CommandSpec<undefined, string>;
|
|
28
27
|
export declare const codexInterruptCommand: CommandSpec<undefined, string>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { once } from 'node:events';
|
|
3
|
-
import {
|
|
3
|
+
import { runClaudeMod } from '../claude-mod.js';
|
|
4
4
|
import { runCodexHook, runCodexInterrupt } from '../codex-hook.js';
|
|
5
5
|
import { runCodexWatch } from '../codex-watch.js';
|
|
6
6
|
import { isSquareError } from '../model.js';
|
|
@@ -135,7 +135,6 @@ export const perceiveCommand = {
|
|
|
135
135
|
let limit;
|
|
136
136
|
let id;
|
|
137
137
|
let takeover = false;
|
|
138
|
-
let now = false;
|
|
139
138
|
const participants = [];
|
|
140
139
|
for (let index = 0; index < argv.length; index++) {
|
|
141
140
|
if (argv[index] === '--id') {
|
|
@@ -164,8 +163,6 @@ export const perceiveCommand = {
|
|
|
164
163
|
}
|
|
165
164
|
else if (argv[index] === '--takeover')
|
|
166
165
|
takeover = true;
|
|
167
|
-
else if (argv[index] === '--now')
|
|
168
|
-
now = true;
|
|
169
166
|
else if (argv[index] !== '--json')
|
|
170
167
|
fail(`✕ perceive does not know ${argv[index]}\nsquare perceive --help`);
|
|
171
168
|
}
|
|
@@ -175,8 +172,6 @@ export const perceiveCommand = {
|
|
|
175
172
|
}
|
|
176
173
|
return { id };
|
|
177
174
|
}
|
|
178
|
-
if (idle !== undefined && now)
|
|
179
|
-
fail('perceive accepts --now or --idle, not both.');
|
|
180
175
|
if (takeover && idle === undefined)
|
|
181
176
|
fail('--takeover can only be used with --idle.');
|
|
182
177
|
return {
|
|
@@ -244,7 +239,7 @@ function historyContinuationArgs(argv) {
|
|
|
244
239
|
continue;
|
|
245
240
|
}
|
|
246
241
|
result.push(flag);
|
|
247
|
-
if (flag === '--from' || flag === '--since' || flag === '--at' || flag === '-
|
|
242
|
+
if (flag === '--from' || flag === '--since' || flag === '--at' || flag === '-C' || flag === '--mention' || flag === '--search' || flag === '--order' || flag === '--format') {
|
|
248
243
|
const value = argv[index + 1];
|
|
249
244
|
if (value !== undefined) {
|
|
250
245
|
result.push(value);
|
|
@@ -261,12 +256,11 @@ function parseHistory(argv, context) {
|
|
|
261
256
|
let after;
|
|
262
257
|
let before;
|
|
263
258
|
const at = [];
|
|
264
|
-
let
|
|
265
|
-
let afterContext;
|
|
259
|
+
let historyContext;
|
|
266
260
|
let mention;
|
|
267
|
-
let
|
|
268
|
-
let
|
|
269
|
-
let
|
|
261
|
+
let full = false;
|
|
262
|
+
let search;
|
|
263
|
+
let regex = false;
|
|
270
264
|
let order;
|
|
271
265
|
let format;
|
|
272
266
|
const participants = [];
|
|
@@ -299,32 +293,20 @@ function parseHistory(argv, context) {
|
|
|
299
293
|
at.push(...values.map((value) => formatActivityId(parseActRef(value, flag))));
|
|
300
294
|
index += 1;
|
|
301
295
|
}
|
|
302
|
-
else if (flag === '-B') {
|
|
303
|
-
beforeContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
|
|
304
|
-
index += 1;
|
|
305
|
-
}
|
|
306
|
-
else if (flag === '-A') {
|
|
307
|
-
afterContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
|
|
308
|
-
index += 1;
|
|
309
|
-
}
|
|
310
296
|
else if (flag === '-C') {
|
|
311
|
-
|
|
312
|
-
beforeContext = context;
|
|
313
|
-
afterContext = context;
|
|
297
|
+
historyContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
|
|
314
298
|
index += 1;
|
|
315
299
|
}
|
|
316
|
-
else if (flag === '--
|
|
317
|
-
|
|
300
|
+
else if (flag === '--full')
|
|
301
|
+
full = true;
|
|
318
302
|
else if (flag === '--mention') {
|
|
319
303
|
mention = requireValue(argv, index, flag);
|
|
320
304
|
index += 1;
|
|
321
305
|
}
|
|
322
|
-
else if (flag === '--
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
else if (flag === '--fixed') {
|
|
327
|
-
fixed = requireValue(argv, index, flag);
|
|
306
|
+
else if (flag === '--regex')
|
|
307
|
+
regex = true;
|
|
308
|
+
else if (flag === '--search') {
|
|
309
|
+
search = requireValue(argv, index, flag);
|
|
328
310
|
index += 1;
|
|
329
311
|
}
|
|
330
312
|
else if (flag === '--order') {
|
|
@@ -341,19 +323,17 @@ function parseHistory(argv, context) {
|
|
|
341
323
|
else if (flag !== '--json')
|
|
342
324
|
fail(`✕ history does not know ${flag}\nsquare history --help`);
|
|
343
325
|
}
|
|
344
|
-
if (
|
|
345
|
-
fail('--
|
|
346
|
-
if (grep === '' || fixed === '')
|
|
347
|
-
fail('--grep and --fixed require non-empty text.');
|
|
326
|
+
if (search === '')
|
|
327
|
+
fail('--search requires non-empty text.');
|
|
348
328
|
if (before !== undefined && after !== undefined)
|
|
349
329
|
fail('--before and --after cannot be combined.');
|
|
350
330
|
return {
|
|
351
331
|
query: { limit, from: participants.length === 0 ? undefined : participants, since,
|
|
352
332
|
cursor: after !== undefined ? { id: after, direction: 'after' } : before === undefined ? undefined : { id: before, direction: 'before' },
|
|
353
333
|
at: at.length === 0 ? undefined : at,
|
|
354
|
-
|
|
355
|
-
search:
|
|
356
|
-
|
|
334
|
+
context: historyContext, mention, order,
|
|
335
|
+
search: search === undefined ? undefined : { text: search, mode: regex ? 'regex' : 'literal' } },
|
|
336
|
+
full, format,
|
|
357
337
|
continuationArgs: historyContinuationArgs(argv),
|
|
358
338
|
};
|
|
359
339
|
}
|
|
@@ -396,7 +376,7 @@ export const historyCommand = {
|
|
|
396
376
|
}
|
|
397
377
|
return renderHistoryResult(squarePath, outcome, {
|
|
398
378
|
query: options.query,
|
|
399
|
-
|
|
379
|
+
full: options.full,
|
|
400
380
|
continuationArgs: options.continuationArgs,
|
|
401
381
|
});
|
|
402
382
|
},
|
|
@@ -470,28 +450,10 @@ function hookCommand(runHook) {
|
|
|
470
450
|
present: (result) => process.stdout.write(result),
|
|
471
451
|
};
|
|
472
452
|
}
|
|
473
|
-
export const
|
|
474
|
-
parse(argv, context) { if (argv.length > 0)
|
|
475
|
-
usage(context.command); return undefined; },
|
|
476
|
-
execute: async () => {
|
|
477
|
-
try {
|
|
478
|
-
return await runClaudeHook(await readStdin(), process.env, text => new Promise((resolve, reject) => {
|
|
479
|
-
process.stdout.write(text, error => { if (error)
|
|
480
|
-
reject(error);
|
|
481
|
-
else
|
|
482
|
-
resolve(); });
|
|
483
|
-
}));
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
return '';
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
present: () => { },
|
|
490
|
-
};
|
|
491
|
-
export const claudeWatchCommand = {
|
|
453
|
+
export const claudeModCommand = {
|
|
492
454
|
parse(argv, context) { if (argv.length > 0)
|
|
493
455
|
usage(context.command); return undefined; },
|
|
494
|
-
execute: async () =>
|
|
456
|
+
execute: async () => runClaudeMod(await readStdin(), process.env),
|
|
495
457
|
present: () => { },
|
|
496
458
|
};
|
|
497
459
|
export const codexHookCommand = {
|
package/dist/cli/registry.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { buildCommand, doneCommand, expressCommand, ignoreCommand, joinCommand, listenCommand,
|
|
1
|
+
import { buildCommand, doneCommand, expressCommand, ignoreCommand, joinCommand, listenCommand, unlistenCommand } from './square-commands.js';
|
|
2
2
|
import { doctorCommand } from './maintenance-commands.js';
|
|
3
3
|
import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
|
|
4
4
|
import { helpCommand, versionCommand } from './meta-commands.js';
|
|
5
5
|
import { mcpServerCommand } from './mcp-server-command.js';
|
|
6
6
|
import { JSON_COMMANDS } from './json.js';
|
|
7
|
-
import { perceiveCommand,
|
|
7
|
+
import { perceiveCommand, claudeModCommand, codexHookCommand, codexWatchCommand, codexInterruptCommand, historyCommand, inboxCommand, listCommand, statusCommand, streamCommand, } from './observation-commands.js';
|
|
8
8
|
/** Every public command is an executable adapter, including aliases and utility commands. */
|
|
9
9
|
export const commandRegistry = [
|
|
10
10
|
{ names: ['build'], spec: buildCommand },
|
|
@@ -12,8 +12,7 @@ export const commandRegistry = [
|
|
|
12
12
|
{ names: ['join'], spec: joinCommand },
|
|
13
13
|
{ names: ['stream'], spec: streamCommand },
|
|
14
14
|
{ names: ['inbox'], spec: inboxCommand },
|
|
15
|
-
{ names: ['claude-
|
|
16
|
-
{ names: ['claude-watch'], spec: claudeWatchCommand },
|
|
15
|
+
{ names: ['claude-mod'], spec: claudeModCommand },
|
|
17
16
|
{ names: ['codex-hook'], spec: codexHookCommand },
|
|
18
17
|
{ names: ['codex-watch'], spec: codexWatchCommand },
|
|
19
18
|
{ names: ['codex-interrupt'], spec: codexInterruptCommand },
|
|
@@ -22,7 +21,6 @@ export const commandRegistry = [
|
|
|
22
21
|
{ names: ['listen'], spec: listenCommand },
|
|
23
22
|
{ names: ['ignore'], spec: ignoreCommand },
|
|
24
23
|
{ names: ['unlisten'], spec: unlistenCommand },
|
|
25
|
-
{ names: ['listening'], spec: listeningCommand },
|
|
26
24
|
{ names: ['done'], spec: doneCommand },
|
|
27
25
|
{ names: ['install'], spec: installCommand },
|
|
28
26
|
{ names: ['uninstall'], spec: uninstallCommand },
|
|
@@ -25,7 +25,7 @@ interface BodyIntent {
|
|
|
25
25
|
body?: string;
|
|
26
26
|
}
|
|
27
27
|
interface ListenerIntent {
|
|
28
|
-
target
|
|
28
|
+
target: string;
|
|
29
29
|
}
|
|
30
30
|
export declare const buildCommand: CommandSpec<BuildIntent, string>;
|
|
31
31
|
export declare const joinCommand: CommandSpec<JoinIntent, string>;
|
|
@@ -33,6 +33,5 @@ export declare const expressCommand: CommandSpec<ActivityIntent>;
|
|
|
33
33
|
export declare const listenCommand: CommandSpec<ListenerIntent, string>;
|
|
34
34
|
export declare const ignoreCommand: CommandSpec<ListenerIntent, string>;
|
|
35
35
|
export declare const unlistenCommand: CommandSpec<ListenerIntent, string>;
|
|
36
|
-
export declare const listeningCommand: CommandSpec<ListenerIntent, string>;
|
|
37
36
|
export declare const doneCommand: CommandSpec<BodyIntent, string>;
|
|
38
37
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatHardCap } from '../model.js';
|
|
2
2
|
import { commandPrefix, participantCommandPrefix, withPathOutput, } from '../presentation.js';
|
|
3
|
-
import { renderJoinResult, renderLifecycleResult, renderListenerChange,
|
|
3
|
+
import { renderJoinResult, renderLifecycleResult, renderListenerChange, } from '../operation-presentation.js';
|
|
4
4
|
import { Square, requireOwner, requireJoinName, selectOwnSeat } from '../square-api.js';
|
|
5
5
|
import { executeExpression } from './express-presentation.js';
|
|
6
6
|
import { jsonResult } from './json.js';
|
|
@@ -153,15 +153,14 @@ export const expressCommand = {
|
|
|
153
153
|
},
|
|
154
154
|
present: () => { },
|
|
155
155
|
};
|
|
156
|
-
function parseListener(argv, context, targetRequired) {
|
|
157
|
-
const args = argv.filter((arg) => arg !== '--json');
|
|
158
|
-
if (targetRequired ? args.length !== 1 : args.length !== 0)
|
|
159
|
-
usage(context.command);
|
|
160
|
-
return targetRequired ? { target: args[0] } : {};
|
|
161
|
-
}
|
|
162
156
|
function listenerCommand(verb) {
|
|
163
157
|
return {
|
|
164
|
-
parse(argv, context) {
|
|
158
|
+
parse(argv, context) {
|
|
159
|
+
const args = argv.filter((arg) => arg !== '--json');
|
|
160
|
+
if (args.length !== 1)
|
|
161
|
+
usage(context.command);
|
|
162
|
+
return { target: args[0] };
|
|
163
|
+
},
|
|
165
164
|
async execute(intent, context) {
|
|
166
165
|
const squarePath = requireSquarePath(context);
|
|
167
166
|
const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name))[verb](intent.target, context.json ? {} : { observe: true }));
|
|
@@ -173,15 +172,6 @@ function listenerCommand(verb) {
|
|
|
173
172
|
export const listenCommand = listenerCommand('listen');
|
|
174
173
|
export const ignoreCommand = listenerCommand('ignore');
|
|
175
174
|
export const unlistenCommand = listenerCommand('unlisten');
|
|
176
|
-
export const listeningCommand = {
|
|
177
|
-
parse(argv, context) { return parseListener(argv, context, false); },
|
|
178
|
-
async execute(_intent, context) {
|
|
179
|
-
const squarePath = requireSquarePath(context);
|
|
180
|
-
const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).listening(context.json ? {} : { observe: true }));
|
|
181
|
-
return context.json ? jsonResult(data) : renderListeningResult(squarePath, data);
|
|
182
|
-
},
|
|
183
|
-
present: (result) => process.stdout.write(result),
|
|
184
|
-
};
|
|
185
175
|
export const doneCommand = {
|
|
186
176
|
parse(argv, context) {
|
|
187
177
|
const args = argv.filter((arg) => arg !== '--json');
|