@pasko70/pibo 1.13.0 → 1.14.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/apps/chat/static-assets.js +3 -1
- package/dist/apps/chat/web-app.js +10 -0
- package/dist/apps/chat-ui/assets/{dist-B_8EcoB6.js → dist-1eQpSfYj.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DVPXynJE.js → dist-BG1J2hGI.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-UYBI2F8E.js → dist-BIwXuAMQ.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-D6pYe6d_.js → chat-ui/assets/dist-BeCHnL7P.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CVLKu7NV.js → dist-CtsdsIpj.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DugRsLaG.js → dist-DfdQQObC.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-BFRDpzcv.js → chat-ui/assets/dist-DmqwP_tM.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-B5mpygOZ.js → chat-ui/assets/dist-LWXXqiGT.js} +1 -1
- package/dist/apps/{context-files-ui/assets/dist-B_zXPNMb.js → chat-ui/assets/dist-hoDORUlL.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-XmG4qG-A.js → dist-ur4yXrlj.js} +1 -1
- package/dist/apps/chat-ui/assets/index-CoCl8FG8.css +1 -0
- package/dist/apps/chat-ui/assets/index-DLNTzhdh.js +237 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-R_7kgvvD.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/{chat-ui/assets/dist-CnX234jg.js → context-files-ui/assets/dist-B7gXtTZC.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-CELRUR8b.js → context-files-ui/assets/dist-BD262Ouw.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-Em3LWmNi.js → dist-BKuGdcnX.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-CVmpu9FK.js → context-files-ui/assets/dist-C-Jm45P4.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-DM6yvMZv.js → dist-CHdx1Jri.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-BLI6sQZG.js → dist-CULMH-fB.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-CLrlJjPP.js → dist-Cjq6RWEt.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-D8ppb7gk.js → dist-CntqFySz.js} +1 -1
- package/dist/apps/context-files-ui/assets/{dist-SEP88Pg7.js → dist-DxEGebCR.js} +1 -1
- package/dist/apps/{chat-ui/assets/dist-KOXty1JB.js → context-files-ui/assets/dist-kPuQud7t.js} +1 -1
- package/dist/apps/context-files-ui/assets/index-DZWsbuvg.js +202 -0
- package/dist/apps/context-files-ui/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.14.0.vsix +0 -0
- package/dist/cli-session/localSessionSource.js +2 -0
- package/dist/core/session-router.js +3 -0
- package/dist/gateway/client.js +8 -0
- package/dist/gateway/server.js +18 -2
- package/dist/gateway/web.js +1 -0
- package/dist/local/extension.js +2 -0
- package/dist/loops/plugin.js +34 -1
- package/dist/loops/service.js +85 -0
- package/dist/mcp/config-command.js +5 -3
- package/dist/plugins/context-files-store.js +287 -101
- package/dist/plugins/context-files.js +102 -154
- package/dist/shared/trace-live-reducer.js +6 -1
- package/dist/web/channel.js +105 -16
- package/dist/web/http.js +14 -2
- package/package.json +11 -7
- package/dist/apps/chat-ui/assets/index-DhFQ1th-.js +0 -237
- package/dist/apps/chat-ui/assets/index-hjygk9Ol.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-1wWonouX.js +0 -41
- package/dist/apps/context-files-ui/assets/index-C7X2UvWe.js +0 -202
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.13.0.vsix +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Pibo Context Files</title>
|
|
7
|
-
<script type="module" crossorigin src="/apps/context-files/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/apps/context-files/assets/index-DZWsbuvg.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/apps/context-files/assets/dist-D1KyNDg5.js">
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/apps/context-files/assets/index-Cs4XQITe.css">
|
|
10
10
|
</head>
|
|
Binary file
|
|
Binary file
|
|
@@ -1050,6 +1050,8 @@ function executionParamsForSlash(command, args) {
|
|
|
1050
1050
|
const trimmed = args?.trim();
|
|
1051
1051
|
if (command === "compact" && trimmed)
|
|
1052
1052
|
return { customInstructions: trimmed };
|
|
1053
|
+
if (command === "goal")
|
|
1054
|
+
return { command: trimmed ?? "" };
|
|
1053
1055
|
if (command === "thinking" && trimmed)
|
|
1054
1056
|
return { level: trimmed };
|
|
1055
1057
|
if (command === "model" && trimmed) {
|
|
@@ -210,6 +210,9 @@ export class PiboSessionRouter {
|
|
|
210
210
|
recoveredRuns: this.runRegistry.listRecoveredRuns(),
|
|
211
211
|
}) ?? []
|
|
212
212
|
: [];
|
|
213
|
+
if (recoveredRuntimeState.length > 0 && options.runtimeInstanceId) {
|
|
214
|
+
console.error(`[pibo] authoritative runtime ${options.runtimeInstanceId} recovered ${recoveredRuntimeState.length} interrupted turn(s)`);
|
|
215
|
+
}
|
|
213
216
|
for (const recovery of recoveredRuntimeState) {
|
|
214
217
|
const session = this.sessionStore.get(recovery.event.piboSessionId);
|
|
215
218
|
if (session)
|
package/dist/gateway/client.js
CHANGED
|
@@ -186,6 +186,14 @@ export async function runGatewayClient(options = {}) {
|
|
|
186
186
|
});
|
|
187
187
|
continue;
|
|
188
188
|
}
|
|
189
|
+
if (text === "/goal" || text.startsWith("/goal ")) {
|
|
190
|
+
writeFrame(socket, {
|
|
191
|
+
type: "req",
|
|
192
|
+
id: randomUUID(),
|
|
193
|
+
event: { type: "execution", piboSessionId, action: "goal", params: { command: text.slice("/goal".length).trim() } },
|
|
194
|
+
});
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
189
197
|
writeFrame(socket, {
|
|
190
198
|
type: "req",
|
|
191
199
|
id: randomUUID(),
|
package/dist/gateway/server.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { createServer } from "node:net";
|
|
2
3
|
import { createDefaultPiboPluginRegistry, createPiboProfileFromRegistryOrDefault, resolvePiboProfileNameFromRegistryOrDefault } from "../plugins/builtin.js";
|
|
3
4
|
import { PiboSessionRouter } from "../core/session-router.js";
|
|
4
5
|
import { createLoopMessagePreflight } from "../loops/store.js";
|
|
5
6
|
import { loadPiboModelDefaults, selectRequestedModelProfile } from "../core/model-defaults.js";
|
|
6
7
|
import { ResourceReaperService } from "../resources/reaper.js";
|
|
8
|
+
import { InMemoryPiboSessionStore } from "../sessions/store.js";
|
|
7
9
|
import { DEFAULT_GATEWAY_HOST, DEFAULT_GATEWAY_PORT, encodeFrame, errorResponse, isGatewayRequestFrame, isGatewaySubscribeFrame, } from "./protocol.js";
|
|
8
10
|
import { releaseFallbackGatewayPid, releaseGatewayPid, writeFallbackGatewayPid, writeGatewayPid } from "./pidfile.js";
|
|
9
11
|
const DEFAULT_MAX_BACKPRESSURE_FRAMES = 1_000;
|
|
@@ -118,12 +120,15 @@ async function createGatewaySessionStore(options) {
|
|
|
118
120
|
const { SqlitePiboSessionStore } = await import("../sessions/sqlite-store.js");
|
|
119
121
|
return new SqlitePiboSessionStore(options.sessionDbPath);
|
|
120
122
|
}
|
|
123
|
+
if (options.persistSession === false)
|
|
124
|
+
return new InMemoryPiboSessionStore();
|
|
121
125
|
const { createDefaultPiboDataSessionStore } = await import("../sessions/pibo-data-store.js");
|
|
122
126
|
return createDefaultPiboDataSessionStore();
|
|
123
127
|
}
|
|
124
128
|
export class PiboGatewayServer {
|
|
125
129
|
options;
|
|
126
130
|
pluginRegistry;
|
|
131
|
+
runtimeInstanceId;
|
|
127
132
|
sessionStore;
|
|
128
133
|
ownsSessionStore = false;
|
|
129
134
|
router;
|
|
@@ -137,6 +142,7 @@ export class PiboGatewayServer {
|
|
|
137
142
|
constructor(options = {}) {
|
|
138
143
|
this.options = options;
|
|
139
144
|
this.pluginRegistry = options.pluginRegistry ?? createDefaultPiboPluginRegistry();
|
|
145
|
+
this.runtimeInstanceId = options.runtimeInstanceId ?? `gateway:${process.pid}:${randomUUID()}`;
|
|
140
146
|
}
|
|
141
147
|
async start() {
|
|
142
148
|
if (this.server)
|
|
@@ -144,12 +150,16 @@ export class PiboGatewayServer {
|
|
|
144
150
|
this.validateChannels();
|
|
145
151
|
this.sessionStore = this.options.sessionStore ?? (await createGatewaySessionStore(this.options));
|
|
146
152
|
this.ownsSessionStore = !this.options.sessionStore;
|
|
153
|
+
const hasExplicitPersistentStore = this.options.sessionStore !== undefined || this.options.sessionDbPath !== undefined;
|
|
154
|
+
const recoverInterruptedRuntimeState = this.options.authoritativeRuntime === true
|
|
155
|
+
&& (this.options.persistSession !== false || hasExplicitPersistentStore);
|
|
147
156
|
this.router = new PiboSessionRouter({
|
|
148
157
|
persistSession: this.options.persistSession,
|
|
149
158
|
pluginRegistry: this.pluginRegistry,
|
|
150
159
|
sessionStore: this.sessionStore,
|
|
151
160
|
messagePreflight: createLoopMessagePreflight({ path: this.options.loopStorePath }),
|
|
152
|
-
recoverInterruptedRuntimeState
|
|
161
|
+
recoverInterruptedRuntimeState,
|
|
162
|
+
runtimeInstanceId: recoverInterruptedRuntimeState ? this.runtimeInstanceId : undefined,
|
|
153
163
|
});
|
|
154
164
|
this.unsubscribe = this.router.subscribe((event) => this.broadcastRouterEvent(event));
|
|
155
165
|
this.server = createServer((socket) => this.handleSocket(socket));
|
|
@@ -270,6 +280,8 @@ export class PiboGatewayServer {
|
|
|
270
280
|
getDiagnostics() {
|
|
271
281
|
const connectionDetails = [...this.connections].map((connection) => ({ ...connection.diagnostics }));
|
|
272
282
|
return {
|
|
283
|
+
runtimeInstanceId: this.runtimeInstanceId,
|
|
284
|
+
authoritativeRuntime: this.options.authoritativeRuntime === true,
|
|
273
285
|
connections: connectionDetails.length,
|
|
274
286
|
slowConnections: connectionDetails.filter((connection) => connection.slow).length,
|
|
275
287
|
droppedEvents: this.droppedRouterEvents,
|
|
@@ -377,7 +389,11 @@ export async function runGatewayServer(options = {}) {
|
|
|
377
389
|
const releasePid = fallbackMode ? releaseFallbackGatewayPid : releaseGatewayPid;
|
|
378
390
|
let server;
|
|
379
391
|
try {
|
|
380
|
-
server = new PiboGatewayServer({
|
|
392
|
+
server = new PiboGatewayServer({
|
|
393
|
+
...options,
|
|
394
|
+
authoritativeRuntime: options.authoritativeRuntime ?? true,
|
|
395
|
+
resourceReaper: resolveGatewayResourceReaperOptions(options),
|
|
396
|
+
});
|
|
381
397
|
await server.start();
|
|
382
398
|
}
|
|
383
399
|
catch (error) {
|
package/dist/gateway/web.js
CHANGED
|
@@ -195,6 +195,7 @@ export async function runWebGatewayServer(options = {}) {
|
|
|
195
195
|
const pluginRegistry = resolvedOptions.pluginRegistry ?? createWebPiboPluginRegistry(resolvedOptions);
|
|
196
196
|
server = new PiboGatewayServer({
|
|
197
197
|
...resolvedOptions,
|
|
198
|
+
authoritativeRuntime: resolvedOptions.authoritativeRuntime ?? true,
|
|
198
199
|
pluginRegistry,
|
|
199
200
|
resourceReaper: resolveGatewayResourceReaperOptions(resolvedOptions),
|
|
200
201
|
loopStorePath: resolvedOptions.chat?.ralphStorePath,
|
package/dist/local/extension.js
CHANGED
|
@@ -202,6 +202,8 @@ function createExecutionParams(slashCommand, args = "") {
|
|
|
202
202
|
}
|
|
203
203
|
if (slashCommand === "/compact")
|
|
204
204
|
return trimmedArgs ? { customInstructions: trimmedArgs } : undefined;
|
|
205
|
+
if (slashCommand === "/goal")
|
|
206
|
+
return { command: trimmedArgs };
|
|
205
207
|
return undefined;
|
|
206
208
|
}
|
|
207
209
|
function formatExecutionResult(event) {
|
package/dist/loops/plugin.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { definePiboPlugin } from '../plugins/registry.js';
|
|
2
|
-
import { createPiboLoopChannel } from './channel.js';
|
|
2
|
+
import { createPiboLoopChannel, getPiboLoopService } from './channel.js';
|
|
3
3
|
import { createBuiltInLoopStopConditions } from './stopping.js';
|
|
4
4
|
import { configurePiboGoalToolStorePath } from './tools.js';
|
|
5
|
+
function objectParams(event) {
|
|
6
|
+
const params = 'params' in event ? event.params : undefined;
|
|
7
|
+
return params && typeof params === 'object' && !Array.isArray(params) ? params : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function parsePiboSessionGoalCommand(event) {
|
|
10
|
+
const command = objectParams(event)?.command;
|
|
11
|
+
if (typeof command !== 'string' || !command.trim())
|
|
12
|
+
throw new Error('Usage: /goal <objective> | /goal pause | /goal resume');
|
|
13
|
+
const normalized = command.trim();
|
|
14
|
+
if (normalized.toLowerCase() === 'pause')
|
|
15
|
+
return { operation: 'pause' };
|
|
16
|
+
if (normalized.toLowerCase() === 'resume')
|
|
17
|
+
return { operation: 'resume' };
|
|
18
|
+
if (normalized.length > 20_000)
|
|
19
|
+
throw new Error('Goal objective is too long');
|
|
20
|
+
return { operation: 'set', objective: normalized };
|
|
21
|
+
}
|
|
5
22
|
export function createPiboLoopPlugin(options = {}) {
|
|
6
23
|
configurePiboGoalToolStorePath(options.loopStorePath);
|
|
7
24
|
return definePiboPlugin({
|
|
@@ -10,6 +27,22 @@ export function createPiboLoopPlugin(options = {}) {
|
|
|
10
27
|
register(api) {
|
|
11
28
|
for (const condition of createBuiltInLoopStopConditions())
|
|
12
29
|
api.registerLoopStopCondition(condition);
|
|
30
|
+
api.registerGatewayAction({
|
|
31
|
+
name: 'goal',
|
|
32
|
+
description: 'Create or update the session Goal Loop. Use /goal pause or /goal resume to control it.',
|
|
33
|
+
slashCommands: ['goal'],
|
|
34
|
+
execute(context, event) {
|
|
35
|
+
const service = getPiboLoopService();
|
|
36
|
+
if (!service)
|
|
37
|
+
throw new Error('Loop service is not running');
|
|
38
|
+
const command = parsePiboSessionGoalCommand(event);
|
|
39
|
+
if (command.operation === 'pause')
|
|
40
|
+
return service.pauseSessionGoal(context.piboSessionId);
|
|
41
|
+
if (command.operation === 'resume')
|
|
42
|
+
return service.resumeSessionGoal(context.piboSessionId);
|
|
43
|
+
return service.setSessionGoal(context.piboSessionId, command.objective);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
13
46
|
api.registerChannel(createPiboLoopChannel(options));
|
|
14
47
|
},
|
|
15
48
|
});
|
package/dist/loops/service.js
CHANGED
|
@@ -44,6 +44,8 @@ function failureRecovery(details) {
|
|
|
44
44
|
return details?.retryable === false ? 'Resolve the reported non-retryable failure, then explicitly restart the Goal.' : 'Pibo will retry automatically after the recorded backoff.';
|
|
45
45
|
}
|
|
46
46
|
function isTerminalGoalStatus(status) { return status === 'complete' || status === 'blocked' || status === 'budget_limited'; }
|
|
47
|
+
function currentGoalStatus(job) { return job.state.goalStatus ?? (job.enabled ? 'active' : 'paused'); }
|
|
48
|
+
function goalName(objective) { return objective.replace(/\s+/g, ' ').trim().slice(0, 80); }
|
|
47
49
|
function retryBackoffMs(consecutiveErrors, baseMs, maxMs, jitterRatio, random) {
|
|
48
50
|
const exponent = Math.max(0, Math.min(30, consecutiveErrors - 1));
|
|
49
51
|
const bounded = Math.min(maxMs, baseMs * (2 ** exponent));
|
|
@@ -145,6 +147,89 @@ export class PiboLoopService {
|
|
|
145
147
|
this.armSoon();
|
|
146
148
|
return this.store.getJob(id);
|
|
147
149
|
}
|
|
150
|
+
setSessionGoal(piboSessionId, objective) {
|
|
151
|
+
const normalizedObjective = objective.trim();
|
|
152
|
+
if (!normalizedObjective)
|
|
153
|
+
throw new Error('Usage: /goal <objective> | /goal pause | /goal resume');
|
|
154
|
+
if (normalizedObjective.length > 20_000)
|
|
155
|
+
throw new Error('Goal objective is too long');
|
|
156
|
+
const session = this.options.context.getSession(piboSessionId);
|
|
157
|
+
if (!session)
|
|
158
|
+
throw new Error('Pibo Session not found');
|
|
159
|
+
const existing = this.store.getSessionGoalOwner(piboSessionId);
|
|
160
|
+
if (!existing) {
|
|
161
|
+
const goal = this.store.createSessionGoal({
|
|
162
|
+
name: goalName(normalizedObjective),
|
|
163
|
+
target: this.sessionGoalTarget(session.metadata?.chatRoomId),
|
|
164
|
+
profile: session.profile,
|
|
165
|
+
prompt: normalizedObjective,
|
|
166
|
+
initialPiboSessionId: piboSessionId,
|
|
167
|
+
}, this.now());
|
|
168
|
+
this.armSoon();
|
|
169
|
+
return { operation: 'created', goal };
|
|
170
|
+
}
|
|
171
|
+
const status = currentGoalStatus(existing);
|
|
172
|
+
if (status === 'complete')
|
|
173
|
+
throw new Error('The current Goal is completing; wait for its active run to finish before creating another Goal');
|
|
174
|
+
if (status === 'blocked')
|
|
175
|
+
throw new Error('The current Goal is blocked; explicitly reopen it before updating or resuming it');
|
|
176
|
+
if (status === 'budget_limited')
|
|
177
|
+
throw new Error('The current Goal is budget limited; increase or clear its token budget before updating and resuming it');
|
|
178
|
+
const goal = this.store.updateJob(existing.id, {
|
|
179
|
+
name: goalName(normalizedObjective),
|
|
180
|
+
prompt: normalizedObjective,
|
|
181
|
+
enabled: true,
|
|
182
|
+
}, this.now());
|
|
183
|
+
if (!goal)
|
|
184
|
+
throw new Error('Goal Loop not found');
|
|
185
|
+
this.armSoon();
|
|
186
|
+
return { operation: 'updated', goal };
|
|
187
|
+
}
|
|
188
|
+
pauseSessionGoal(piboSessionId) {
|
|
189
|
+
const existing = this.requireSessionGoal(piboSessionId);
|
|
190
|
+
const status = currentGoalStatus(existing);
|
|
191
|
+
if (status === 'paused')
|
|
192
|
+
return { operation: 'already-paused', goal: existing };
|
|
193
|
+
if (status !== 'active')
|
|
194
|
+
throw new Error(`Goal Loop cannot be paused while its status is ${status}`);
|
|
195
|
+
const goal = this.store.requestStop(existing.id, this.now());
|
|
196
|
+
if (!goal)
|
|
197
|
+
throw new Error('Goal Loop not found');
|
|
198
|
+
this.armSoon();
|
|
199
|
+
return { operation: 'paused', goal };
|
|
200
|
+
}
|
|
201
|
+
resumeSessionGoal(piboSessionId) {
|
|
202
|
+
const existing = this.requireSessionGoal(piboSessionId);
|
|
203
|
+
const status = currentGoalStatus(existing);
|
|
204
|
+
if (status === 'active' && existing.enabled)
|
|
205
|
+
return { operation: 'already-active', goal: existing };
|
|
206
|
+
if (status !== 'paused')
|
|
207
|
+
throw new Error(`Goal Loop cannot be resumed while its status is ${status}`);
|
|
208
|
+
const goal = this.store.updateJob(existing.id, { enabled: true }, this.now());
|
|
209
|
+
if (!goal)
|
|
210
|
+
throw new Error('Goal Loop not found');
|
|
211
|
+
this.armSoon();
|
|
212
|
+
return { operation: 'resumed', goal };
|
|
213
|
+
}
|
|
214
|
+
requireSessionGoal(piboSessionId) {
|
|
215
|
+
if (!this.options.context.getSession(piboSessionId))
|
|
216
|
+
throw new Error('Pibo Session not found');
|
|
217
|
+
const goal = this.store.getSessionGoalOwner(piboSessionId);
|
|
218
|
+
if (!goal)
|
|
219
|
+
throw new Error('This Pibo Session has no Goal Loop');
|
|
220
|
+
return goal;
|
|
221
|
+
}
|
|
222
|
+
sessionGoalTarget(value) {
|
|
223
|
+
if (typeof value !== 'string' || !value.trim())
|
|
224
|
+
return { kind: 'default-chat' };
|
|
225
|
+
const roomId = value.trim();
|
|
226
|
+
const room = this.roomService.getRoom(roomId);
|
|
227
|
+
if (!room)
|
|
228
|
+
throw new Error('The Pibo Session room no longer exists');
|
|
229
|
+
if (isPiboRoomArchived(room))
|
|
230
|
+
throw new Error('Archived rooms are read-only');
|
|
231
|
+
return { kind: 'room', roomId };
|
|
232
|
+
}
|
|
148
233
|
repairGoalSessionMetadata() {
|
|
149
234
|
for (const session of this.options.context.listSessions?.() ?? []) {
|
|
150
235
|
if (session.metadata?.loopMode === 'goal' && (session.metadata.loopJobId !== undefined || session.metadata.loopRunId !== undefined))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { ensureConfigExists, findConfigPath, getPreferredConfigPath, } from './config.js';
|
|
3
4
|
import { ErrorCode, formatCliError } from './errors.js';
|
|
@@ -155,9 +156,10 @@ export async function configCommand(options) {
|
|
|
155
156
|
return;
|
|
156
157
|
}
|
|
157
158
|
if (options.action === 'init') {
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
159
|
+
const preferredPath = getPreferredConfigPath(options.configPath);
|
|
160
|
+
const alreadyExists = existsSync(preferredPath);
|
|
161
|
+
const path = await ensureConfigExists(preferredPath);
|
|
162
|
+
console.log(alreadyExists ? `MCP config ready: ${path}` : `Created MCP config: ${path}`);
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
163
165
|
if (options.action === 'describe') {
|