sentinelayer-cli 0.4.4 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +996 -998
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -63
- package/src/agents/jules/config/definition.js +160 -209
- package/src/agents/jules/config/system-prompt.js +182 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -377
- package/src/agents/jules/loop.js +450 -367
- package/src/agents/jules/pulse.js +10 -327
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +309 -308
- package/src/agents/jules/tools/aidenid-email.js +189 -0
- package/src/agents/jules/tools/auth-audit.js +1691 -557
- package/src/agents/jules/tools/dispatch.js +335 -327
- package/src/agents/jules/tools/file-edit.js +2 -180
- package/src/agents/jules/tools/file-read.js +2 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -168
- package/src/agents/jules/tools/grep.js +2 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -161
- package/src/agents/jules/tools/runtime-audit.js +507 -503
- package/src/agents/jules/tools/shell.js +2 -383
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/persona-visuals.js +61 -0
- package/src/agents/shared-tools/dispatch-core.js +315 -0
- package/src/agents/shared-tools/file-edit.js +180 -0
- package/src/agents/shared-tools/file-read.js +100 -0
- package/src/agents/shared-tools/glob.js +168 -0
- package/src/agents/shared-tools/grep.js +228 -0
- package/src/agents/shared-tools/index.js +46 -0
- package/src/agents/shared-tools/path-guards.js +161 -0
- package/src/agents/shared-tools/shell.js +383 -0
- package/src/ai/aidenid.js +1009 -972
- package/src/ai/client.js +553 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -0
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +371 -126
- package/src/auth/http.js +611 -270
- package/src/auth/service.js +1106 -891
- package/src/auth/session-store.js +813 -359
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +419 -375
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +29 -21
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +872 -866
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/fix-cycle.js +377 -0
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -0
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/interactive/index.js +97 -95
- package/src/legacy-cli.js +2994 -2592
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -118
- package/src/review/ai-review.js +679 -669
- package/src/review/local-review.js +1305 -1295
- package/src/review/omargate-interactive.js +68 -0
- package/src/review/omargate-orchestrator.js +300 -0
- package/src/review/persona-prompts.js +296 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/scan-modes.js +42 -0
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -118
- package/src/telemetry/sync.js +203 -199
- package/src/ui/command-hints.js +13 -0
- package/src/ui/markdown.js +220 -220
package/src/commands/chat.js
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
import fsp from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
|
|
5
|
-
import pc from "picocolors";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
createMultiProviderApiClient,
|
|
9
|
-
resolveModel,
|
|
10
|
-
resolveProvider,
|
|
11
|
-
} from "../ai/client.js";
|
|
12
|
-
import { resolveOutputRoot } from "../config/service.js";
|
|
13
|
-
|
|
14
|
-
function shouldEmitJson(options, command) {
|
|
15
|
-
const local = Boolean(options && options.json);
|
|
16
|
-
const globalFromCommand =
|
|
17
|
-
command && command.optsWithGlobals ? Boolean(command.optsWithGlobals().json) : false;
|
|
18
|
-
return local || globalFromCommand;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function createSessionId() {
|
|
22
|
-
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+/, "").replace("T", "-");
|
|
23
|
-
const random = Math.random().toString(36).slice(2, 8);
|
|
24
|
-
return `${stamp}-${random}`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function estimateTokens(text) {
|
|
28
|
-
const normalized = String(text || "");
|
|
29
|
-
if (!normalized) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
return Math.max(1, Math.ceil(normalized.length / 4));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function readPromptFromStdin() {
|
|
36
|
-
if (process.stdin.isTTY) {
|
|
37
|
-
return "";
|
|
38
|
-
}
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
let buffer = "";
|
|
41
|
-
process.stdin.setEncoding("utf-8");
|
|
42
|
-
process.stdin.on("data", (chunk) => {
|
|
43
|
-
buffer += String(chunk || "");
|
|
44
|
-
});
|
|
45
|
-
process.stdin.on("error", reject);
|
|
46
|
-
process.stdin.on("end", () => resolve(buffer.trim()));
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function appendTranscriptEntries({ transcriptPath, entries } = {}) {
|
|
51
|
-
await fsp.mkdir(path.dirname(transcriptPath), { recursive: true });
|
|
52
|
-
const rows = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
53
|
-
await fsp.appendFile(transcriptPath, `${rows}\n`, "utf-8");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function registerChatCommand(program) {
|
|
57
|
-
const chat = program
|
|
58
|
-
.command("chat")
|
|
59
|
-
.description("Low-latency chat command surface for guided AI interaction");
|
|
60
|
-
|
|
61
|
-
chat
|
|
62
|
-
.command("ask")
|
|
63
|
-
.description("Send one prompt and stream/store the response")
|
|
64
|
-
.option("--prompt <text>", "Prompt text. If omitted, reads from STDIN when piped.")
|
|
65
|
-
.option("--provider <provider>", "Provider override (openai|anthropic|google)")
|
|
66
|
-
.option("--model <model>", "Model override")
|
|
67
|
-
.option("--api-key <key>", "Provider API key override")
|
|
68
|
-
.option("--path <path>", "Workspace path for output/config resolution", ".")
|
|
69
|
-
.option("--output-dir <path>", "Optional artifact output root override")
|
|
70
|
-
.option("--session-id <id>", "Optional existing session id (append mode)")
|
|
71
|
-
.option("--dry-run", "Skip network call and emit deterministic simulated response")
|
|
72
|
-
.option("--no-stream", "Disable streaming output")
|
|
73
|
-
.option("--json", "Emit machine-readable output")
|
|
74
|
-
.action(async (options, command) => {
|
|
75
|
-
const emitJson = shouldEmitJson(options, command);
|
|
76
|
-
const promptFromFlag = String(options.prompt || "").trim();
|
|
77
|
-
const promptFromStdin = promptFromFlag ? "" : await readPromptFromStdin();
|
|
78
|
-
const prompt = String(promptFromFlag || promptFromStdin).trim();
|
|
79
|
-
if (!prompt) {
|
|
80
|
-
throw new Error("Prompt is required. Use --prompt or pipe text to STDIN.");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
84
|
-
const outputRoot = await resolveOutputRoot({
|
|
85
|
-
cwd: targetPath,
|
|
86
|
-
outputDirOverride: options.outputDir,
|
|
87
|
-
env: process.env,
|
|
88
|
-
});
|
|
89
|
-
const sessionId = String(options.sessionId || "").trim() || createSessionId();
|
|
90
|
-
const transcriptPath = path.join(outputRoot, "chat", "sessions", `${sessionId}.jsonl`);
|
|
91
|
-
|
|
92
|
-
const provider = resolveProvider({
|
|
93
|
-
provider: options.provider,
|
|
94
|
-
env: process.env,
|
|
95
|
-
});
|
|
96
|
-
const model = resolveModel({
|
|
97
|
-
provider,
|
|
98
|
-
model: options.model,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const startedAt = Date.now();
|
|
102
|
-
let responseText = "";
|
|
103
|
-
|
|
104
|
-
if (options.dryRun) {
|
|
105
|
-
responseText = `DRY_RUN_RESPONSE: ${prompt.slice(0, 240)}`;
|
|
106
|
-
} else {
|
|
107
|
-
const streamEnabled = Boolean(options.stream);
|
|
108
|
-
let streamedText = "";
|
|
109
|
-
const client = createMultiProviderApiClient();
|
|
110
|
-
const invocation = await client.invoke({
|
|
111
|
-
provider,
|
|
112
|
-
model,
|
|
113
|
-
prompt,
|
|
114
|
-
apiKey: options.apiKey,
|
|
115
|
-
stream: streamEnabled,
|
|
116
|
-
env: process.env,
|
|
117
|
-
onChunk: streamEnabled
|
|
118
|
-
? (chunk) => {
|
|
119
|
-
streamedText += chunk;
|
|
120
|
-
if (!emitJson) {
|
|
121
|
-
process.stdout.write(chunk);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
: undefined,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
responseText = streamEnabled ? streamedText || invocation.text : invocation.text;
|
|
128
|
-
if (streamEnabled && !emitJson) {
|
|
129
|
-
process.stdout.write("\n");
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const durationMs = Date.now() - startedAt;
|
|
134
|
-
const generatedAt = new Date().toISOString();
|
|
135
|
-
const inputTokens = estimateTokens(prompt);
|
|
136
|
-
const outputTokens = estimateTokens(responseText);
|
|
137
|
-
|
|
138
|
-
await appendTranscriptEntries({
|
|
139
|
-
transcriptPath,
|
|
140
|
-
entries: [
|
|
141
|
-
{
|
|
142
|
-
timestamp: generatedAt,
|
|
143
|
-
role: "user",
|
|
144
|
-
provider,
|
|
145
|
-
model,
|
|
146
|
-
session_id: sessionId,
|
|
147
|
-
content: prompt,
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
timestamp: generatedAt,
|
|
151
|
-
role: "assistant",
|
|
152
|
-
provider,
|
|
153
|
-
model,
|
|
154
|
-
session_id: sessionId,
|
|
155
|
-
dry_run: Boolean(options.dryRun),
|
|
156
|
-
content: responseText,
|
|
157
|
-
},
|
|
158
|
-
],
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const payload = {
|
|
162
|
-
command: "chat ask",
|
|
163
|
-
sessionId,
|
|
164
|
-
provider,
|
|
165
|
-
model,
|
|
166
|
-
dryRun: Boolean(options.dryRun),
|
|
167
|
-
streamed: Boolean(options.stream),
|
|
168
|
-
transcriptPath,
|
|
169
|
-
prompt,
|
|
170
|
-
response: responseText,
|
|
171
|
-
usage: {
|
|
172
|
-
inputTokens,
|
|
173
|
-
outputTokens,
|
|
174
|
-
totalTokens: inputTokens + outputTokens,
|
|
175
|
-
durationMs,
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
if (emitJson) {
|
|
180
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!options.stream || options.dryRun) {
|
|
185
|
-
console.log(responseText);
|
|
186
|
-
}
|
|
187
|
-
console.log(pc.gray(`session: ${sessionId}`));
|
|
188
|
-
console.log(pc.gray(`transcript: ${transcriptPath}`));
|
|
189
|
-
console.log(pc.gray(`usage: input=${inputTokens} output=${outputTokens} duration_ms=${durationMs}`));
|
|
190
|
-
});
|
|
191
|
-
}
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createMultiProviderApiClient,
|
|
9
|
+
resolveModel,
|
|
10
|
+
resolveProvider,
|
|
11
|
+
} from "../ai/client.js";
|
|
12
|
+
import { resolveOutputRoot } from "../config/service.js";
|
|
13
|
+
|
|
14
|
+
function shouldEmitJson(options, command) {
|
|
15
|
+
const local = Boolean(options && options.json);
|
|
16
|
+
const globalFromCommand =
|
|
17
|
+
command && command.optsWithGlobals ? Boolean(command.optsWithGlobals().json) : false;
|
|
18
|
+
return local || globalFromCommand;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createSessionId() {
|
|
22
|
+
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+/, "").replace("T", "-");
|
|
23
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
24
|
+
return `${stamp}-${random}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function estimateTokens(text) {
|
|
28
|
+
const normalized = String(text || "");
|
|
29
|
+
if (!normalized) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
return Math.max(1, Math.ceil(normalized.length / 4));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function readPromptFromStdin() {
|
|
36
|
+
if (process.stdin.isTTY) {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
let buffer = "";
|
|
41
|
+
process.stdin.setEncoding("utf-8");
|
|
42
|
+
process.stdin.on("data", (chunk) => {
|
|
43
|
+
buffer += String(chunk || "");
|
|
44
|
+
});
|
|
45
|
+
process.stdin.on("error", reject);
|
|
46
|
+
process.stdin.on("end", () => resolve(buffer.trim()));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function appendTranscriptEntries({ transcriptPath, entries } = {}) {
|
|
51
|
+
await fsp.mkdir(path.dirname(transcriptPath), { recursive: true });
|
|
52
|
+
const rows = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
53
|
+
await fsp.appendFile(transcriptPath, `${rows}\n`, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerChatCommand(program) {
|
|
57
|
+
const chat = program
|
|
58
|
+
.command("chat")
|
|
59
|
+
.description("Low-latency chat command surface for guided AI interaction");
|
|
60
|
+
|
|
61
|
+
chat
|
|
62
|
+
.command("ask")
|
|
63
|
+
.description("Send one prompt and stream/store the response")
|
|
64
|
+
.option("--prompt <text>", "Prompt text. If omitted, reads from STDIN when piped.")
|
|
65
|
+
.option("--provider <provider>", "Provider override (openai|anthropic|google)")
|
|
66
|
+
.option("--model <model>", "Model override")
|
|
67
|
+
.option("--api-key <key>", "Provider API key override")
|
|
68
|
+
.option("--path <path>", "Workspace path for output/config resolution", ".")
|
|
69
|
+
.option("--output-dir <path>", "Optional artifact output root override")
|
|
70
|
+
.option("--session-id <id>", "Optional existing session id (append mode)")
|
|
71
|
+
.option("--dry-run", "Skip network call and emit deterministic simulated response")
|
|
72
|
+
.option("--no-stream", "Disable streaming output")
|
|
73
|
+
.option("--json", "Emit machine-readable output")
|
|
74
|
+
.action(async (options, command) => {
|
|
75
|
+
const emitJson = shouldEmitJson(options, command);
|
|
76
|
+
const promptFromFlag = String(options.prompt || "").trim();
|
|
77
|
+
const promptFromStdin = promptFromFlag ? "" : await readPromptFromStdin();
|
|
78
|
+
const prompt = String(promptFromFlag || promptFromStdin).trim();
|
|
79
|
+
if (!prompt) {
|
|
80
|
+
throw new Error("Prompt is required. Use --prompt or pipe text to STDIN.");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
84
|
+
const outputRoot = await resolveOutputRoot({
|
|
85
|
+
cwd: targetPath,
|
|
86
|
+
outputDirOverride: options.outputDir,
|
|
87
|
+
env: process.env,
|
|
88
|
+
});
|
|
89
|
+
const sessionId = String(options.sessionId || "").trim() || createSessionId();
|
|
90
|
+
const transcriptPath = path.join(outputRoot, "chat", "sessions", `${sessionId}.jsonl`);
|
|
91
|
+
|
|
92
|
+
const provider = resolveProvider({
|
|
93
|
+
provider: options.provider,
|
|
94
|
+
env: process.env,
|
|
95
|
+
});
|
|
96
|
+
const model = resolveModel({
|
|
97
|
+
provider,
|
|
98
|
+
model: options.model,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const startedAt = Date.now();
|
|
102
|
+
let responseText = "";
|
|
103
|
+
|
|
104
|
+
if (options.dryRun) {
|
|
105
|
+
responseText = `DRY_RUN_RESPONSE: ${prompt.slice(0, 240)}`;
|
|
106
|
+
} else {
|
|
107
|
+
const streamEnabled = Boolean(options.stream);
|
|
108
|
+
let streamedText = "";
|
|
109
|
+
const client = createMultiProviderApiClient();
|
|
110
|
+
const invocation = await client.invoke({
|
|
111
|
+
provider,
|
|
112
|
+
model,
|
|
113
|
+
prompt,
|
|
114
|
+
apiKey: options.apiKey,
|
|
115
|
+
stream: streamEnabled,
|
|
116
|
+
env: process.env,
|
|
117
|
+
onChunk: streamEnabled
|
|
118
|
+
? (chunk) => {
|
|
119
|
+
streamedText += chunk;
|
|
120
|
+
if (!emitJson) {
|
|
121
|
+
process.stdout.write(chunk);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
: undefined,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
responseText = streamEnabled ? streamedText || invocation.text : invocation.text;
|
|
128
|
+
if (streamEnabled && !emitJson) {
|
|
129
|
+
process.stdout.write("\n");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const durationMs = Date.now() - startedAt;
|
|
134
|
+
const generatedAt = new Date().toISOString();
|
|
135
|
+
const inputTokens = estimateTokens(prompt);
|
|
136
|
+
const outputTokens = estimateTokens(responseText);
|
|
137
|
+
|
|
138
|
+
await appendTranscriptEntries({
|
|
139
|
+
transcriptPath,
|
|
140
|
+
entries: [
|
|
141
|
+
{
|
|
142
|
+
timestamp: generatedAt,
|
|
143
|
+
role: "user",
|
|
144
|
+
provider,
|
|
145
|
+
model,
|
|
146
|
+
session_id: sessionId,
|
|
147
|
+
content: prompt,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
timestamp: generatedAt,
|
|
151
|
+
role: "assistant",
|
|
152
|
+
provider,
|
|
153
|
+
model,
|
|
154
|
+
session_id: sessionId,
|
|
155
|
+
dry_run: Boolean(options.dryRun),
|
|
156
|
+
content: responseText,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const payload = {
|
|
162
|
+
command: "chat ask",
|
|
163
|
+
sessionId,
|
|
164
|
+
provider,
|
|
165
|
+
model,
|
|
166
|
+
dryRun: Boolean(options.dryRun),
|
|
167
|
+
streamed: Boolean(options.stream),
|
|
168
|
+
transcriptPath,
|
|
169
|
+
prompt,
|
|
170
|
+
response: responseText,
|
|
171
|
+
usage: {
|
|
172
|
+
inputTokens,
|
|
173
|
+
outputTokens,
|
|
174
|
+
totalTokens: inputTokens + outputTokens,
|
|
175
|
+
durationMs,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (emitJson) {
|
|
180
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!options.stream || options.dryRun) {
|
|
185
|
+
console.log(responseText);
|
|
186
|
+
}
|
|
187
|
+
console.log(pc.gray(`session: ${sessionId}`));
|
|
188
|
+
console.log(pc.gray(`transcript: ${transcriptPath}`));
|
|
189
|
+
console.log(pc.gray(`usage: input=${inputTokens} output=${outputTokens} duration_ms=${durationMs}`));
|
|
190
|
+
});
|
|
191
|
+
}
|