@rk0429/agentic-relay 21.3.0 → 22.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/chat/chat-daemon-service.d.ts +88 -0
- package/dist/application/chat/chat-daemon-service.js +358 -0
- package/dist/application/chat/chat-daemon-service.js.map +1 -0
- package/dist/application/chat/chat-inbound-handler.d.ts +35 -0
- package/dist/application/chat/chat-inbound-handler.js +193 -0
- package/dist/application/chat/chat-inbound-handler.js.map +1 -0
- package/dist/application/chat/chat-logging.d.ts +8 -0
- package/dist/application/chat/chat-logging.js +16 -0
- package/dist/application/chat/chat-logging.js.map +1 -0
- package/dist/application/chat/chat-outbound-handler.d.ts +17 -0
- package/dist/application/chat/chat-outbound-handler.js +207 -0
- package/dist/application/chat/chat-outbound-handler.js.map +1 -0
- package/dist/application/chat/chat-question-service.d.ts +37 -0
- package/dist/application/chat/chat-question-service.js +135 -0
- package/dist/application/chat/chat-question-service.js.map +1 -0
- package/dist/application/chat/chat-setup-service.d.ts +27 -0
- package/dist/application/chat/chat-setup-service.js +183 -0
- package/dist/application/chat/chat-setup-service.js.map +1 -0
- package/dist/application/chat/message-queue-manager.d.ts +13 -0
- package/dist/application/chat/message-queue-manager.js +34 -0
- package/dist/application/chat/message-queue-manager.js.map +1 -0
- package/dist/application/housekeeping.js +6 -2
- package/dist/application/housekeeping.js.map +1 -1
- package/dist/application/routine-status-query-service.d.ts +17 -0
- package/dist/application/routine-status-query-service.js +111 -0
- package/dist/application/routine-status-query-service.js.map +1 -1
- package/dist/bin/relay.d.ts +4 -2
- package/dist/bin/relay.js +301 -12
- package/dist/bin/relay.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -1
- package/dist/domain/chat/message-cleaner.d.ts +11 -0
- package/dist/domain/chat/message-cleaner.js +54 -0
- package/dist/domain/chat/message-cleaner.js.map +1 -0
- package/dist/domain/chat/message-router.d.ts +8 -0
- package/dist/domain/chat/message-router.js +97 -0
- package/dist/domain/chat/message-router.js.map +1 -0
- package/dist/domain/chat/message-splitter.d.ts +3 -0
- package/dist/domain/chat/message-splitter.js +148 -0
- package/dist/domain/chat/message-splitter.js.map +1 -0
- package/dist/domain/chat/platform-connection.d.ts +31 -0
- package/dist/domain/chat/platform-connection.js +67 -0
- package/dist/domain/chat/platform-connection.js.map +1 -0
- package/dist/domain/chat/platform-types.d.ts +18 -0
- package/dist/domain/chat/platform-types.js +14 -0
- package/dist/domain/chat/platform-types.js.map +1 -0
- package/dist/domain/chat/ports.d.ts +31 -0
- package/dist/domain/chat/ports.js +2 -0
- package/dist/domain/chat/ports.js.map +1 -0
- package/dist/domain/chat/session-bridge.d.ts +41 -0
- package/dist/domain/chat/session-bridge.js +93 -0
- package/dist/domain/chat/session-bridge.js.map +1 -0
- package/dist/domain/chat/types.d.ts +85 -0
- package/dist/domain/chat/types.js +2 -0
- package/dist/domain/chat/types.js.map +1 -0
- package/dist/domain/chat/user-question.d.ts +33 -0
- package/dist/domain/chat/user-question.js +73 -0
- package/dist/domain/chat/user-question.js.map +1 -0
- package/dist/domain/config.d.ts +3 -1
- package/dist/domain/config.js +9 -0
- package/dist/domain/config.js.map +1 -1
- package/dist/domain/routine-loader.d.ts +2 -0
- package/dist/domain/routine-loader.js +3 -0
- package/dist/domain/routine-loader.js.map +1 -1
- package/dist/infrastructure/chat/adapter-factory.d.ts +5 -0
- package/dist/infrastructure/chat/adapter-factory.js +15 -0
- package/dist/infrastructure/chat/adapter-factory.js.map +1 -0
- package/dist/infrastructure/chat/adapters/discord-adapter.d.ts +23 -0
- package/dist/infrastructure/chat/adapters/discord-adapter.js +199 -0
- package/dist/infrastructure/chat/adapters/discord-adapter.js.map +1 -0
- package/dist/infrastructure/chat/adapters/slack-adapter.d.ts +24 -0
- package/dist/infrastructure/chat/adapters/slack-adapter.js +155 -0
- package/dist/infrastructure/chat/adapters/slack-adapter.js.map +1 -0
- package/dist/infrastructure/chat/agent-gateway-impl.d.ts +21 -0
- package/dist/infrastructure/chat/agent-gateway-impl.js +101 -0
- package/dist/infrastructure/chat/agent-gateway-impl.js.map +1 -0
- package/dist/infrastructure/chat/chat-ipc-client.d.ts +33 -0
- package/dist/infrastructure/chat/chat-ipc-client.js +70 -0
- package/dist/infrastructure/chat/chat-ipc-client.js.map +1 -0
- package/dist/infrastructure/chat/chat-ipc-server.d.ts +19 -0
- package/dist/infrastructure/chat/chat-ipc-server.js +125 -0
- package/dist/infrastructure/chat/chat-ipc-server.js.map +1 -0
- package/dist/infrastructure/chat/chat-logger.d.ts +24 -0
- package/dist/infrastructure/chat/chat-logger.js +86 -0
- package/dist/infrastructure/chat/chat-logger.js.map +1 -0
- package/dist/infrastructure/chat/credentials-repository.d.ts +8 -0
- package/dist/infrastructure/chat/credentials-repository.js +84 -0
- package/dist/infrastructure/chat/credentials-repository.js.map +1 -0
- package/dist/infrastructure/chat/session-bridge-repository.d.ts +13 -0
- package/dist/infrastructure/chat/session-bridge-repository.js +120 -0
- package/dist/infrastructure/chat/session-bridge-repository.js.map +1 -0
- package/dist/infrastructure/chat/token-masking.d.ts +3 -0
- package/dist/infrastructure/chat/token-masking.js +22 -0
- package/dist/infrastructure/chat/token-masking.js.map +1 -0
- package/dist/infrastructure/store/relay-store.js +3 -1
- package/dist/infrastructure/store/relay-store.js.map +1 -1
- package/dist/interfaces/cli/chat-cli.d.ts +34 -0
- package/dist/interfaces/cli/chat-cli.js +68 -0
- package/dist/interfaces/cli/chat-cli.js.map +1 -0
- package/dist/interfaces/cli/relay-cli-args.d.ts +16 -1
- package/dist/interfaces/cli/relay-cli-args.js +135 -2
- package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
- package/dist/interfaces/mcp/chat-tools.d.ts +34 -0
- package/dist/interfaces/mcp/chat-tools.js +131 -0
- package/dist/interfaces/mcp/chat-tools.js.map +1 -0
- package/dist/interfaces/mcp/relay-mcp-server.js +43 -6
- package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
- package/package.json +5 -1
- package/scripts/check-chat-boundary.mjs +220 -0
- package/scripts/check-chat-boundary.test.ts +139 -0
- package/scripts/check-circular-deps.mjs +199 -0
- package/scripts/check-circular-deps.test.ts +62 -0
package/dist/bin/relay.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --disable-warning=ExperimentalWarning
|
|
2
|
-
import { fork } from "node:child_process";
|
|
2
|
+
import { execFile, fork } from "node:child_process";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { access, readFile, realpath, rm } from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { promisify } from "node:util";
|
|
10
|
+
import { ChatDaemonService, } from "../application/chat/chat-daemon-service.js";
|
|
11
|
+
import { ChatInboundHandler } from "../application/chat/chat-inbound-handler.js";
|
|
12
|
+
import { ChatOutboundHandler } from "../application/chat/chat-outbound-handler.js";
|
|
13
|
+
import { ChatQuestionService, InMemoryUserQuestionRepository, } from "../application/chat/chat-question-service.js";
|
|
14
|
+
import { ChatSetupService } from "../application/chat/chat-setup-service.js";
|
|
15
|
+
import { MessageQueueManager } from "../application/chat/message-queue-manager.js";
|
|
9
16
|
import { CoreManagementService, } from "../application/core-management-service.js";
|
|
10
17
|
import { LoopEventLogSink } from "../application/event-log-sink.js";
|
|
11
18
|
import { isProcessAlive, reconcileAbandonedRunningTaskSessions, runStartupHousekeeping, } from "../application/housekeeping.js";
|
|
@@ -24,24 +31,33 @@ import { VscodeSetupService } from "../application/vscode-setup-service.js";
|
|
|
24
31
|
import { WorkflowExecutionService, } from "../application/workflow-execution-service.js";
|
|
25
32
|
import { ValidationError } from "../core/errors.js";
|
|
26
33
|
import { buildCrossBackendHandoffPrompt, createCrossBackendHandoffInfo, } from "../domain/handoff.js";
|
|
34
|
+
import { resolveChatConfig } from "../domain/config.js";
|
|
27
35
|
import { resolveRole } from "../domain/role.js";
|
|
28
36
|
import { resolveBackend, validateRoutingRules } from "../domain/routing.js";
|
|
29
37
|
import { loadRoutine } from "../domain/routine-loader.js";
|
|
30
38
|
import { BackendRegistry } from "../infrastructure/backends/backend-registry.js";
|
|
39
|
+
import { AdapterFactory } from "../infrastructure/chat/adapter-factory.js";
|
|
40
|
+
import { AgentGatewayImpl } from "../infrastructure/chat/agent-gateway-impl.js";
|
|
41
|
+
import { ChatIpcServer } from "../infrastructure/chat/chat-ipc-server.js";
|
|
42
|
+
import { ChatLogger } from "../infrastructure/chat/chat-logger.js";
|
|
43
|
+
import { CredentialsFileRepository } from "../infrastructure/chat/credentials-repository.js";
|
|
44
|
+
import { SessionBridgeFileRepository } from "../infrastructure/chat/session-bridge-repository.js";
|
|
31
45
|
import { CommandExecutionGateway } from "../infrastructure/command-execution-gateway.js";
|
|
32
46
|
import { CliBackendExecutor } from "../infrastructure/backends/cli-backend-executor.js";
|
|
33
47
|
import { ChildProcessExecutor, ChildProcessInteractiveRunner, } from "../infrastructure/process/process-executor.js";
|
|
34
48
|
import { LoopStateRepository } from "../infrastructure/store/loop-state-repository.js";
|
|
35
49
|
import { RelayStore } from "../infrastructure/store/relay-store.js";
|
|
36
|
-
import { isMissingFileError } from "../infrastructure/store/file-utils.js";
|
|
50
|
+
import { isMissingFileError, writeJsonAtomic } from "../infrastructure/store/file-utils.js";
|
|
37
51
|
import { RoutineExecutionLeaseRepository } from "../infrastructure/store/routine-execution-lease-repository.js";
|
|
38
52
|
import { RoutineRuntimeProjectionRepository } from "../infrastructure/store/routine-runtime-projection-repository.js";
|
|
39
53
|
import { RoutineStateRepository } from "../infrastructure/store/routine-state-repository.js";
|
|
54
|
+
import { handleChatCommand, } from "../interfaces/cli/chat-cli.js";
|
|
40
55
|
import { parseRelayCliArgs, } from "../interfaces/cli/relay-cli-args.js";
|
|
41
56
|
import { createLoopProgressReporter } from "../interfaces/cli/loop-progress-shell.js";
|
|
42
57
|
import { runRelayInteractiveShell } from "../interfaces/cli/relay-shell.js";
|
|
43
58
|
import { renderVisualization } from "../interfaces/cli/visualization-renderer.js";
|
|
44
59
|
import { createRelayMcpServer, serveRelayMcpServer, } from "../interfaces/mcp/relay-mcp-server.js";
|
|
60
|
+
const execFileAsync = promisify(execFile);
|
|
45
61
|
export const AGENTIC_SETUP_PROMPT = "ワークスペースのエージェント基盤をセットアップしてください。 .agents/skills/agentic-setup/SKILL.md の手順に従って実行してください。";
|
|
46
62
|
export function buildRoutineStatusQueryService(options) {
|
|
47
63
|
return new RoutineStatusQueryService({
|
|
@@ -52,6 +68,9 @@ export function buildRoutineStatusQueryService(options) {
|
|
|
52
68
|
stateRepo: new RoutineStateRepository(options.store),
|
|
53
69
|
runtimeRepo: new RoutineRuntimeProjectionRepository(options.store),
|
|
54
70
|
store: options.store,
|
|
71
|
+
leaseRepo: new RoutineExecutionLeaseRepository(options.store),
|
|
72
|
+
loopStateFiles: () => options.store.listLoopStateFiles(),
|
|
73
|
+
deleteLoopState: (filename) => options.store.deleteLoopState(filename),
|
|
55
74
|
});
|
|
56
75
|
}
|
|
57
76
|
async function main() {
|
|
@@ -184,9 +203,78 @@ async function main() {
|
|
|
184
203
|
workflowRepo,
|
|
185
204
|
configDir,
|
|
186
205
|
});
|
|
206
|
+
const createChatSetupService = () => new ChatSetupService({
|
|
207
|
+
credentialsRepository: new CredentialsFileRepository(store.rootDir),
|
|
208
|
+
prompt: promptFromStdin,
|
|
209
|
+
confirm: confirmFromStdin,
|
|
210
|
+
openUrl: openUrlInBrowser,
|
|
211
|
+
logger: (message) => writeLine(process.stderr, message),
|
|
212
|
+
});
|
|
213
|
+
const createChatDaemonService = () => {
|
|
214
|
+
const credentialsRepository = new CredentialsFileRepository(store.rootDir);
|
|
215
|
+
const sessionBridgeRepository = new SessionBridgeFileRepository(store.rootDir);
|
|
216
|
+
const outboundHandler = new ChatOutboundHandler();
|
|
217
|
+
const questionService = new ChatQuestionService({
|
|
218
|
+
userQuestionRepository: new InMemoryUserQuestionRepository(),
|
|
219
|
+
});
|
|
220
|
+
const spawnService = new SpawnAgentsService({
|
|
221
|
+
backendRegistry,
|
|
222
|
+
backendExecutor,
|
|
223
|
+
store,
|
|
224
|
+
taskService,
|
|
225
|
+
cwd,
|
|
226
|
+
env: process.env,
|
|
227
|
+
});
|
|
228
|
+
const inboundHandler = new ChatInboundHandler({
|
|
229
|
+
agentGateway: new AgentGatewayImpl(spawnService, taskService, store, backendRegistry),
|
|
230
|
+
sessionBridgeRepository,
|
|
231
|
+
outboundHandler,
|
|
232
|
+
questionService,
|
|
233
|
+
messageQueueManager: new MessageQueueManager(),
|
|
234
|
+
logger: (message) => writeLine(process.stderr, message),
|
|
235
|
+
});
|
|
236
|
+
const ipcServer = new ChatIpcServer({
|
|
237
|
+
rootDir: store.rootDir,
|
|
238
|
+
outboundHandler,
|
|
239
|
+
questionService,
|
|
240
|
+
});
|
|
241
|
+
return new ChatDaemonService({
|
|
242
|
+
store,
|
|
243
|
+
credentialsRepository,
|
|
244
|
+
sessionBridgeRepository,
|
|
245
|
+
adapterFactory: new AdapterFactory(),
|
|
246
|
+
resolveChatConfig,
|
|
247
|
+
outboundHandler,
|
|
248
|
+
questionService,
|
|
249
|
+
inboundHandler,
|
|
250
|
+
ipcServer,
|
|
251
|
+
getProcessStatus: async (pid) => {
|
|
252
|
+
if (!isProcessAlive(pid)) {
|
|
253
|
+
return { alive: false };
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const { stdout } = await execFileAsync("ps", ["-o", "lstart=", "-p", String(pid)]);
|
|
257
|
+
const startedAt = stdout.trim();
|
|
258
|
+
const parsed = new Date(startedAt);
|
|
259
|
+
return {
|
|
260
|
+
alive: true,
|
|
261
|
+
startedAt: Number.isNaN(parsed.getTime()) ? null : parsed.toISOString(),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return {
|
|
266
|
+
alive: true,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
writeJsonAtomic,
|
|
271
|
+
createLogger: (options) => new ChatLogger(options),
|
|
272
|
+
});
|
|
273
|
+
};
|
|
187
274
|
if (command.kind === "routine-run" ||
|
|
188
275
|
command.kind === "routine-start" ||
|
|
189
276
|
command.kind === "routine-stop" ||
|
|
277
|
+
command.kind === "routine-cleanup" ||
|
|
190
278
|
command.kind === "routine-list" ||
|
|
191
279
|
command.kind === "routine-status") {
|
|
192
280
|
const exitCode = await handleRoutineCommand(command, {
|
|
@@ -217,6 +305,26 @@ async function main() {
|
|
|
217
305
|
}
|
|
218
306
|
return;
|
|
219
307
|
}
|
|
308
|
+
if (command.kind === "chat-setup" ||
|
|
309
|
+
command.kind === "chat-start" ||
|
|
310
|
+
command.kind === "chat-stop" ||
|
|
311
|
+
command.kind === "chat-status") {
|
|
312
|
+
const exitCode = await handleChatCommand(command, {
|
|
313
|
+
cwd,
|
|
314
|
+
argv: process.argv.slice(2),
|
|
315
|
+
scriptPath: process.argv[1] ?? fileURLToPath(import.meta.url),
|
|
316
|
+
env: process.env,
|
|
317
|
+
stdout: process.stdout,
|
|
318
|
+
stderr: process.stderr,
|
|
319
|
+
createSetupService: createChatSetupService,
|
|
320
|
+
createDaemonService: createChatDaemonService,
|
|
321
|
+
forkProcess: (modulePath, args, options) => fork(modulePath, args, options),
|
|
322
|
+
});
|
|
323
|
+
if (exitCode !== undefined) {
|
|
324
|
+
process.exitCode = exitCode;
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
220
328
|
if (command.kind === "mcp") {
|
|
221
329
|
const installed = await backendRegistry.detectInstalled();
|
|
222
330
|
if (installed.length === 0) {
|
|
@@ -246,6 +354,7 @@ async function main() {
|
|
|
246
354
|
routineStatusQueryService: buildRoutineStatusQueryService({
|
|
247
355
|
store,
|
|
248
356
|
workflowRepo,
|
|
357
|
+
configDir: command.configDir,
|
|
249
358
|
}),
|
|
250
359
|
allowSpawnAgents: process.env.RELAY_ALLOW_SPAWN_AGENTS !== "0",
|
|
251
360
|
shutdownSignal: shutdownController.signal,
|
|
@@ -351,9 +460,9 @@ export async function handleRoutineCommand(command, dependencies) {
|
|
|
351
460
|
const placeholderTime = new Date().toISOString();
|
|
352
461
|
warnOnBuiltInRoutineVarOverrides(command.vars, dependencies.stderr);
|
|
353
462
|
const routineVars = buildRoutineLoadVars(command.vars, placeholderTime);
|
|
354
|
-
await dependencies.runStartupHousekeeping(dependencies.store, {
|
|
463
|
+
await suppressLoopStateWarnings(() => dependencies.runStartupHousekeeping(dependencies.store, {
|
|
355
464
|
protectedLoopStateFile: command.resumeStateFile,
|
|
356
|
-
});
|
|
465
|
+
}));
|
|
357
466
|
await dependencies.reconcileAbandonedRunningTaskSessions(dependencies.store, dependencies.taskService);
|
|
358
467
|
const loadedRoutine = await dependencies.loadRoutine(workflowPath, dependencies.workflowRepo, routineVars);
|
|
359
468
|
warnOnBuiltInRoutineYamlVarOverrides(loadedRoutine.rawYamlVarKeys, dependencies.stderr);
|
|
@@ -375,7 +484,7 @@ export async function handleRoutineCommand(command, dependencies) {
|
|
|
375
484
|
vars: command.vars ?? {},
|
|
376
485
|
})
|
|
377
486
|
: undefined;
|
|
378
|
-
dependencies.stdout.write(renderRoutineDryRun(loadedRoutine, workflowPath, resumePreflight));
|
|
487
|
+
dependencies.stdout.write(renderRoutineDryRun(loadedRoutine, workflowPath, resumePreflight, command.vars, loadedRoutine.rawYamlVars));
|
|
379
488
|
return 0;
|
|
380
489
|
}
|
|
381
490
|
enforceAllowCommandRun(workflowPath, loadedRoutine.capabilitySummary, command.allowCommandRun ?? false);
|
|
@@ -481,6 +590,26 @@ export async function handleRoutineCommand(command, dependencies) {
|
|
|
481
590
|
await stopRoutineDaemon(dependencies);
|
|
482
591
|
return 0;
|
|
483
592
|
}
|
|
593
|
+
case "routine-cleanup": {
|
|
594
|
+
const service = dependencies.createRoutineStatusQueryService({
|
|
595
|
+
configDir: resolveRoutineConfigDir(command.configDir, dependencies.store, dependencies.cwd),
|
|
596
|
+
});
|
|
597
|
+
const result = await service.cleanup(command.dryRun ?? false);
|
|
598
|
+
if (result.targets.length === 0) {
|
|
599
|
+
dependencies.stderr.write("No cleanup targets found.\n");
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
if (command.dryRun) {
|
|
603
|
+
dependencies.stderr.write("Cleanup targets (dry-run):\n");
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
dependencies.stderr.write(`Cleaned up ${result.deleted} file(s):\n`);
|
|
607
|
+
}
|
|
608
|
+
for (const target of result.targets) {
|
|
609
|
+
dependencies.stderr.write(` ${target.kind}: ${target.filePath} — ${target.reason}\n`);
|
|
610
|
+
}
|
|
611
|
+
return 0;
|
|
612
|
+
}
|
|
484
613
|
case "routine-list": {
|
|
485
614
|
const service = dependencies.createRoutineStatusQueryService({
|
|
486
615
|
configDir: resolveRoutineConfigDir(command.configDir, dependencies.store, dependencies.cwd),
|
|
@@ -501,11 +630,31 @@ export async function handleRoutineCommand(command, dependencies) {
|
|
|
501
630
|
}
|
|
502
631
|
}
|
|
503
632
|
}
|
|
633
|
+
async function suppressLoopStateWarnings(fn) {
|
|
634
|
+
const origWarn = console.warn;
|
|
635
|
+
console.warn = (...args) => {
|
|
636
|
+
const msg = typeof args[0] === "string" ? args[0] : "";
|
|
637
|
+
if (msg.startsWith("Ignoring invalid loop state file")) {
|
|
638
|
+
console.debug(...args);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
origWarn.apply(console, args);
|
|
642
|
+
};
|
|
643
|
+
try {
|
|
644
|
+
return await fn();
|
|
645
|
+
}
|
|
646
|
+
finally {
|
|
647
|
+
console.warn = origWarn;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
504
650
|
export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(import.meta.url))) {
|
|
505
651
|
if (topic) {
|
|
506
652
|
if (isRoutineHelpTopic(topic)) {
|
|
507
653
|
return renderRoutineHelp(topic, scriptName);
|
|
508
654
|
}
|
|
655
|
+
if (isChatHelpTopic(topic)) {
|
|
656
|
+
return renderChatHelp(topic, scriptName);
|
|
657
|
+
}
|
|
509
658
|
return renderCoreSubcommandHelp(topic, scriptName);
|
|
510
659
|
}
|
|
511
660
|
const commandName = path.parse(scriptName).name;
|
|
@@ -514,8 +663,13 @@ export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(i
|
|
|
514
663
|
` ${scriptName} routine run <path> [--config <dir>] [--var key=value ...] [--dry-run] [--resume-state <file>] [--force-resume] [--allow-command-run]`,
|
|
515
664
|
` ${scriptName} routine start [--config <dir>] [--daemon] [--allow-command-run]`,
|
|
516
665
|
` ${scriptName} routine stop`,
|
|
666
|
+
` ${scriptName} routine cleanup [--config <dir>] [--dry-run]`,
|
|
517
667
|
` ${scriptName} routine list [--config <dir>] [--format <table|tsv|json>]`,
|
|
518
668
|
` ${scriptName} routine status [name] [--config <dir>] [--format <table|tsv|json>] [--limit <n>]`,
|
|
669
|
+
` ${scriptName} chat setup (--slack | --discord)`,
|
|
670
|
+
` ${scriptName} chat start [--foreground]`,
|
|
671
|
+
` ${scriptName} chat stop`,
|
|
672
|
+
` ${scriptName} chat status`,
|
|
519
673
|
` ${scriptName} core <setup|update|remove> [options]`,
|
|
520
674
|
` ${scriptName} setup-vscode [--target <code|code-insiders|cursor>] [--uninstall]`,
|
|
521
675
|
` ${scriptName} visualize`,
|
|
@@ -527,8 +681,13 @@ export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(i
|
|
|
527
681
|
` ${commandName} routine run [options] Run one routine YAML immediately`,
|
|
528
682
|
` ${commandName} routine start [options] Start the routine daemon`,
|
|
529
683
|
` ${commandName} routine stop Stop the routine daemon`,
|
|
684
|
+
` ${commandName} routine cleanup [options] Remove stale routine metadata`,
|
|
530
685
|
` ${commandName} routine list [options] List known routines`,
|
|
531
686
|
` ${commandName} routine status [options] Show routine status details`,
|
|
687
|
+
` ${commandName} chat setup [options] Configure Slack or Discord integration`,
|
|
688
|
+
` ${commandName} chat start [options] Start the chat daemon`,
|
|
689
|
+
` ${commandName} chat stop Stop the chat daemon`,
|
|
690
|
+
` ${commandName} chat status Show chat daemon status`,
|
|
532
691
|
` ${commandName} core setup [options] Setup .agents/ from agentic-core dist`,
|
|
533
692
|
` ${commandName} core update [options] Update .agents/ from latest dist`,
|
|
534
693
|
` ${commandName} core remove [options] Remove relay core managed files`,
|
|
@@ -584,6 +743,82 @@ function renderCoreSubcommandHelp(topic, scriptName) {
|
|
|
584
743
|
function isRoutineHelpTopic(topic) {
|
|
585
744
|
return topic.startsWith("routine");
|
|
586
745
|
}
|
|
746
|
+
function isChatHelpTopic(topic) {
|
|
747
|
+
return topic.startsWith("chat");
|
|
748
|
+
}
|
|
749
|
+
function renderChatHelp(topic, scriptName) {
|
|
750
|
+
if (topic === "chat") {
|
|
751
|
+
return [
|
|
752
|
+
`Usage: ${scriptName} chat setup (--slack | --discord)`,
|
|
753
|
+
` ${scriptName} chat start [--foreground]`,
|
|
754
|
+
` ${scriptName} chat stop`,
|
|
755
|
+
` ${scriptName} chat status`,
|
|
756
|
+
"",
|
|
757
|
+
"Chat commands:",
|
|
758
|
+
" setup Configure Slack or Discord credentials",
|
|
759
|
+
" start Start the chat daemon",
|
|
760
|
+
" stop Stop the chat daemon",
|
|
761
|
+
" status Show chat daemon status",
|
|
762
|
+
"",
|
|
763
|
+
"Options:",
|
|
764
|
+
" --slack Setup Slack integration",
|
|
765
|
+
" --discord Setup Discord integration",
|
|
766
|
+
" --foreground Run the chat daemon in the foreground",
|
|
767
|
+
" -h, --help Show this help",
|
|
768
|
+
].join("\n");
|
|
769
|
+
}
|
|
770
|
+
const commandName = path.parse(scriptName).name;
|
|
771
|
+
const allSubcommandsReference = `See '${commandName} chat --help' for all subcommands.`;
|
|
772
|
+
switch (topic) {
|
|
773
|
+
case "chat-setup":
|
|
774
|
+
return [
|
|
775
|
+
`Usage: ${scriptName} chat setup (--slack | --discord)`,
|
|
776
|
+
"",
|
|
777
|
+
"Configure Slack or Discord credentials for the chat daemon.",
|
|
778
|
+
"",
|
|
779
|
+
"Options:",
|
|
780
|
+
" --slack Configure Slack",
|
|
781
|
+
" --discord Configure Discord",
|
|
782
|
+
" -h, --help Show this help",
|
|
783
|
+
"",
|
|
784
|
+
allSubcommandsReference,
|
|
785
|
+
].join("\n");
|
|
786
|
+
case "chat-start":
|
|
787
|
+
return [
|
|
788
|
+
`Usage: ${scriptName} chat start [--foreground]`,
|
|
789
|
+
"",
|
|
790
|
+
"Start the chat daemon.",
|
|
791
|
+
"",
|
|
792
|
+
"Options:",
|
|
793
|
+
" --foreground Run in the foreground for debugging",
|
|
794
|
+
" -h, --help Show this help",
|
|
795
|
+
"",
|
|
796
|
+
allSubcommandsReference,
|
|
797
|
+
].join("\n");
|
|
798
|
+
case "chat-stop":
|
|
799
|
+
return [
|
|
800
|
+
`Usage: ${scriptName} chat stop`,
|
|
801
|
+
"",
|
|
802
|
+
"Stop the chat daemon.",
|
|
803
|
+
"",
|
|
804
|
+
"Options:",
|
|
805
|
+
" -h, --help Show this help",
|
|
806
|
+
"",
|
|
807
|
+
allSubcommandsReference,
|
|
808
|
+
].join("\n");
|
|
809
|
+
case "chat-status":
|
|
810
|
+
return [
|
|
811
|
+
`Usage: ${scriptName} chat status`,
|
|
812
|
+
"",
|
|
813
|
+
"Show chat daemon status.",
|
|
814
|
+
"",
|
|
815
|
+
"Options:",
|
|
816
|
+
" -h, --help Show this help",
|
|
817
|
+
"",
|
|
818
|
+
allSubcommandsReference,
|
|
819
|
+
].join("\n");
|
|
820
|
+
}
|
|
821
|
+
}
|
|
587
822
|
function renderRoutineHelp(topic, scriptName) {
|
|
588
823
|
if (topic === "routine") {
|
|
589
824
|
return renderRoutineAggregateHelp(scriptName);
|
|
@@ -633,6 +868,19 @@ function renderRoutineHelp(topic, scriptName) {
|
|
|
633
868
|
"",
|
|
634
869
|
allSubcommandsReference,
|
|
635
870
|
].join("\n");
|
|
871
|
+
case "routine-cleanup":
|
|
872
|
+
return [
|
|
873
|
+
`Usage: ${scriptName} routine cleanup [options]`,
|
|
874
|
+
"",
|
|
875
|
+
"Remove stale routine metadata files.",
|
|
876
|
+
"",
|
|
877
|
+
"Options:",
|
|
878
|
+
" --config <dir> Override the routine config directory",
|
|
879
|
+
" --dry-run Show cleanup targets without deleting them",
|
|
880
|
+
" -h, --help Show this help",
|
|
881
|
+
"",
|
|
882
|
+
allSubcommandsReference,
|
|
883
|
+
].join("\n");
|
|
636
884
|
case "routine-list":
|
|
637
885
|
return [
|
|
638
886
|
`Usage: ${scriptName} routine list [options]`,
|
|
@@ -655,7 +903,7 @@ function renderRoutineHelp(topic, scriptName) {
|
|
|
655
903
|
"Options:",
|
|
656
904
|
" --config <dir> Override the routine config directory",
|
|
657
905
|
" --format <fmt> Output format (table, tsv, json)",
|
|
658
|
-
" --limit <n> Limit the number of status log entries",
|
|
906
|
+
" --limit <n> Limit the number of status log entries (default: 10)",
|
|
659
907
|
" -h, --help Show this help",
|
|
660
908
|
"",
|
|
661
909
|
allSubcommandsReference,
|
|
@@ -667,6 +915,7 @@ function renderRoutineAggregateHelp(scriptName) {
|
|
|
667
915
|
`Usage: ${scriptName} routine run <workflow.yaml> [options]`,
|
|
668
916
|
` ${scriptName} routine start [options]`,
|
|
669
917
|
` ${scriptName} routine stop`,
|
|
918
|
+
` ${scriptName} routine cleanup [options]`,
|
|
670
919
|
` ${scriptName} routine list [options]`,
|
|
671
920
|
` ${scriptName} routine status [name] [options]`,
|
|
672
921
|
"",
|
|
@@ -674,6 +923,7 @@ function renderRoutineAggregateHelp(scriptName) {
|
|
|
674
923
|
" run Run one routine YAML immediately",
|
|
675
924
|
" start Start the routine daemon",
|
|
676
925
|
" stop Stop the routine daemon",
|
|
926
|
+
" cleanup Remove stale routine metadata",
|
|
677
927
|
" list List known routines",
|
|
678
928
|
" status Show routine status details",
|
|
679
929
|
"",
|
|
@@ -684,9 +934,9 @@ function renderRoutineAggregateHelp(scriptName) {
|
|
|
684
934
|
" --allow-command-run Allow trusted workflows that declare command.run",
|
|
685
935
|
" --resume-state <file> Resume a loop-capable routine from checkpoint state",
|
|
686
936
|
" --force-resume Override workflow digest mismatch during resume",
|
|
687
|
-
" --dry-run Render routine structure without executing
|
|
937
|
+
" --dry-run Render routine structure or cleanup targets without executing deletion",
|
|
688
938
|
" --var key=value Set template variables for one routine run",
|
|
689
|
-
" --limit <n> Limit the number of status log entries",
|
|
939
|
+
" --limit <n> Limit the number of status log entries (default: 10)",
|
|
690
940
|
" -h, --help Show this help",
|
|
691
941
|
].join("\n");
|
|
692
942
|
}
|
|
@@ -953,6 +1203,9 @@ function printRoutineRunResult(result, writer = process.stderr) {
|
|
|
953
1203
|
writeLine(writer, lines.join("\n"));
|
|
954
1204
|
}
|
|
955
1205
|
const BUILT_IN_ROUTINE_VAR_KEYS = ["scheduled_time", "trigger_time"];
|
|
1206
|
+
function isBuiltInRoutineVarKey(key) {
|
|
1207
|
+
return BUILT_IN_ROUTINE_VAR_KEYS.includes(key);
|
|
1208
|
+
}
|
|
956
1209
|
function buildRoutineLoadVars(vars, placeholderValue) {
|
|
957
1210
|
return {
|
|
958
1211
|
...(vars ?? {}),
|
|
@@ -1034,7 +1287,18 @@ async function validateRoutineDryRunResumeState(options) {
|
|
|
1034
1287
|
});
|
|
1035
1288
|
}
|
|
1036
1289
|
}
|
|
1037
|
-
function renderRoutineDryRun(loadedRoutine, workflowPath, resumePreflight) {
|
|
1290
|
+
function renderRoutineDryRun(loadedRoutine, workflowPath, resumePreflight, vars, yamlVars) {
|
|
1291
|
+
const cliVarKeys = new Set(Object.keys(vars ?? {}));
|
|
1292
|
+
const emptyVarWarnings = [
|
|
1293
|
+
...Object.entries(vars ?? {})
|
|
1294
|
+
.filter(([key, value]) => value === "" && !isBuiltInRoutineVarKey(key))
|
|
1295
|
+
.map(([key]) => key),
|
|
1296
|
+
...Object.entries(yamlVars ?? {})
|
|
1297
|
+
.filter(([key, value]) => value === "" &&
|
|
1298
|
+
!isBuiltInRoutineVarKey(key) &&
|
|
1299
|
+
!cliVarKeys.has(key))
|
|
1300
|
+
.map(([key]) => key),
|
|
1301
|
+
].map((key) => ` - Template variable "{{${key}}}" resolved to empty string.`);
|
|
1038
1302
|
const lines = [
|
|
1039
1303
|
`Routine: ${loadedRoutine.routineDefinition.name.value}`,
|
|
1040
1304
|
...(loadedRoutine.routineDefinition.description
|
|
@@ -1058,6 +1322,7 @@ function renderRoutineDryRun(loadedRoutine, workflowPath, resumePreflight) {
|
|
|
1058
1322
|
: []),
|
|
1059
1323
|
]
|
|
1060
1324
|
: []),
|
|
1325
|
+
...(emptyVarWarnings.length > 0 ? ["Warnings:", ...emptyVarWarnings] : []),
|
|
1061
1326
|
"",
|
|
1062
1327
|
renderDryRun(loadedRoutine.routineDefinition.workflow, workflowPath),
|
|
1063
1328
|
];
|
|
@@ -1087,7 +1352,7 @@ function renderRoutineList(result, format = "table") {
|
|
|
1087
1352
|
formatOptionalTimestamp(entry.nextScheduledAt),
|
|
1088
1353
|
formatRoutineStatusWithOrphan(entry.status, entry.orphan),
|
|
1089
1354
|
].join("\t")),
|
|
1090
|
-
...invalid.map((entry) => `#
|
|
1355
|
+
...invalid.map((entry) => `# ${path.basename(entry.yamlPath)}: ${firstLine(stripYamlPathPrefix(entry.yamlPath, entry.error))}`),
|
|
1091
1356
|
];
|
|
1092
1357
|
return `${lines.join("\n")}\n`;
|
|
1093
1358
|
}
|
|
@@ -1117,10 +1382,14 @@ function renderRoutineList(result, format = "table") {
|
|
|
1117
1382
|
}
|
|
1118
1383
|
function renderInvalidRoutineFiles(invalid) {
|
|
1119
1384
|
return [
|
|
1120
|
-
`Skipped ${invalid.length}
|
|
1121
|
-
...invalid.map((entry) => ` - ${path.basename(entry.yamlPath)}: ${firstLine(entry.error)}`),
|
|
1385
|
+
`Skipped (${invalid.length}):`,
|
|
1386
|
+
...invalid.map((entry) => ` - ${path.basename(entry.yamlPath)}: ${firstLine(stripYamlPathPrefix(entry.yamlPath, entry.error))}`),
|
|
1122
1387
|
].join("\n");
|
|
1123
1388
|
}
|
|
1389
|
+
function stripYamlPathPrefix(yamlPath, error) {
|
|
1390
|
+
const prefix = `${yamlPath}: `;
|
|
1391
|
+
return error.startsWith(prefix) ? error.slice(prefix.length) : error;
|
|
1392
|
+
}
|
|
1124
1393
|
function firstLine(value) {
|
|
1125
1394
|
return value.split(/\r?\n/u, 1)[0] ?? "";
|
|
1126
1395
|
}
|
|
@@ -1446,6 +1715,26 @@ function confirmFromStdin(message) {
|
|
|
1446
1715
|
});
|
|
1447
1716
|
});
|
|
1448
1717
|
}
|
|
1718
|
+
function promptFromStdin(message) {
|
|
1719
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
1720
|
+
return new Promise((resolve) => {
|
|
1721
|
+
rl.question(message, (answer) => {
|
|
1722
|
+
rl.close();
|
|
1723
|
+
resolve(answer);
|
|
1724
|
+
});
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
async function openUrlInBrowser(url) {
|
|
1728
|
+
if (process.platform === "darwin") {
|
|
1729
|
+
await execFileAsync("open", [url]);
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (process.platform === "win32") {
|
|
1733
|
+
await execFileAsync("cmd", ["/c", "start", "", url]);
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
await execFileAsync("xdg-open", [url]);
|
|
1737
|
+
}
|
|
1449
1738
|
async function isExecutedAsScript(metaUrl) {
|
|
1450
1739
|
const entryPath = process.argv[1];
|
|
1451
1740
|
if (!entryPath) {
|