@swarmcraftai/cli 0.1.1 → 0.1.5
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 +8 -6
- package/dist/main.cjs +468 -112
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,17 +57,19 @@ Core commands:
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
swarmcraft auth login --email <email>
|
|
60
|
-
swarmcraft
|
|
61
|
-
swarmcraft
|
|
62
|
-
swarmcraft
|
|
60
|
+
swarmcraft delegation start --user <customer-email-or-id> --reason <text> --expires-in-minutes 600
|
|
61
|
+
swarmcraft projects list --delegation <delegation-id>
|
|
62
|
+
swarmcraft init --project <project-id> --workspace <path> --delegation <delegation-id>
|
|
63
|
+
swarmcraft one-shot --project <project-id> --workspace <path> --agent-provider codex --delegation <delegation-id>
|
|
63
64
|
swarmcraft one-shot --project <project-id> --workspace <path> --agent-provider codex --run-id <known-run-id>
|
|
64
|
-
swarmcraft resume --run <run-id> --workspace <path>
|
|
65
|
-
swarmcraft status --run <run-id> --workspace <path>
|
|
65
|
+
swarmcraft resume --run <run-id> --workspace <path> --delegation <delegation-id>
|
|
66
|
+
swarmcraft status --run <run-id> --workspace <path> --delegation <delegation-id>
|
|
67
|
+
swarmcraft delegation end --delegation <delegation-id>
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
`swarmcraft init` validates the workspace git state, seeds the shared SwarmCraft repo guidance files, materializes non-done ticket packets, links those packets back to SwarmCraft, and prints the next commands.
|
|
69
71
|
|
|
70
|
-
`swarmcraft one-shot` bootstraps the workspace, runs doing
|
|
72
|
+
`swarmcraft one-shot` bootstraps the workspace, runs the doing lane through the selected agent provider, syncs packet updates back to SwarmCraft, writes `.swarmcraft/runs/<run-id>/manifest.json`, streams progress to the terminal, writes agent logs under `.swarmcraft/runs/<run-id>/logs/`, and moves accepted work to `done` by default. Customer one-shots should normally pair this with `--commit-policy done` so each completed ticket creates a local commit. Use `--checking on` or `--reviewing on` only for internal validation runs that deliberately need those extra lanes.
|
|
71
73
|
|
|
72
74
|
Useful safety and support commands:
|
|
73
75
|
|
package/dist/main.cjs
CHANGED
|
@@ -9,6 +9,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
9
9
|
var __commonJS = (cb, mod) => function __require() {
|
|
10
10
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
11
|
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
12
16
|
var __copyProps = (to, from, except, desc) => {
|
|
13
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -25,6 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
29
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
30
|
mod
|
|
27
31
|
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
33
|
|
|
29
34
|
// ../../packages/packet-workflow/dist/assistantActions.js
|
|
30
35
|
var require_assistantActions = __commonJS({
|
|
@@ -2922,7 +2927,8 @@ var require_seedProjectFiles = __commonJS({
|
|
|
2922
2927
|
const existingEndIndex = existingContents.indexOf(renderProjectGuide_1.MANAGED_SECTION_END);
|
|
2923
2928
|
if (existingStartIndex >= 0 && existingEndIndex >= 0 && existingEndIndex > existingStartIndex) {
|
|
2924
2929
|
const replacementEndIndex = existingEndIndex + renderProjectGuide_1.MANAGED_SECTION_END.length;
|
|
2925
|
-
const
|
|
2930
|
+
const afterManagedSection = existingContents.slice(replacementEndIndex).replace(/^\n+/, "\n");
|
|
2931
|
+
const updatedContents = `${existingContents.slice(0, existingStartIndex)}${managedSection}${afterManagedSection}`;
|
|
2926
2932
|
return normalizeTrailingNewline(updatedContents);
|
|
2927
2933
|
}
|
|
2928
2934
|
const separator = existingContents.endsWith("\n\n") ? "" : existingContents.endsWith("\n") ? "\n" : "\n\n";
|
|
@@ -2971,6 +2977,13 @@ var require_dist2 = __commonJS({
|
|
|
2971
2977
|
});
|
|
2972
2978
|
|
|
2973
2979
|
// src/main.ts
|
|
2980
|
+
var main_exports = {};
|
|
2981
|
+
__export(main_exports, {
|
|
2982
|
+
findCommand: () => findCommand,
|
|
2983
|
+
main: () => main,
|
|
2984
|
+
parseArgs: () => parseArgs
|
|
2985
|
+
});
|
|
2986
|
+
module.exports = __toCommonJS(main_exports);
|
|
2974
2987
|
var import_promises4 = require("node:fs/promises");
|
|
2975
2988
|
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
2976
2989
|
var import_promises5 = require("node:readline/promises");
|
|
@@ -2980,7 +2993,7 @@ var import_packet_workflow3 = __toESM(require_dist(), 1);
|
|
|
2980
2993
|
// package.json
|
|
2981
2994
|
var package_default = {
|
|
2982
2995
|
name: "@swarmcraftai/cli",
|
|
2983
|
-
version: "0.1.
|
|
2996
|
+
version: "0.1.5",
|
|
2984
2997
|
description: "Prepare repositories and run packet-based SwarmCraft agent workflows from the terminal.",
|
|
2985
2998
|
license: "SEE LICENSE IN LICENSE.txt",
|
|
2986
2999
|
type: "module",
|
|
@@ -3023,6 +3036,7 @@ var package_default = {
|
|
|
3023
3036
|
// src/apiClient.ts
|
|
3024
3037
|
var API_REQUEST_TIMEOUT_MS = 15e3;
|
|
3025
3038
|
var CLIENT_SOURCE_HEADER = "X-SwarmCraft-Client";
|
|
3039
|
+
var DELEGATION_HEADER = "X-SwarmCraft-Delegation";
|
|
3026
3040
|
var REFRESH_TOKEN_COOKIE_NAME = "swarmcraft_refresh_token";
|
|
3027
3041
|
var SwarmCraftApiError = class extends Error {
|
|
3028
3042
|
status;
|
|
@@ -3074,56 +3088,88 @@ var SwarmCraftClient = class {
|
|
|
3074
3088
|
accessToken
|
|
3075
3089
|
);
|
|
3076
3090
|
}
|
|
3077
|
-
|
|
3078
|
-
return this.requestJson(
|
|
3091
|
+
startDelegation(accessToken, request) {
|
|
3092
|
+
return this.requestJson(
|
|
3093
|
+
"/support/delegations",
|
|
3094
|
+
{
|
|
3095
|
+
method: "POST",
|
|
3096
|
+
body: JSON.stringify(request)
|
|
3097
|
+
},
|
|
3098
|
+
accessToken
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
loadDelegation(accessToken, delegationId) {
|
|
3102
|
+
return this.requestJson(
|
|
3103
|
+
`/support/delegations/${encodeURIComponent(delegationId)}`,
|
|
3104
|
+
{ method: "GET" },
|
|
3105
|
+
accessToken
|
|
3106
|
+
);
|
|
3107
|
+
}
|
|
3108
|
+
endDelegation(accessToken, delegationId) {
|
|
3109
|
+
return this.requestJson(
|
|
3110
|
+
`/support/delegations/${encodeURIComponent(delegationId)}`,
|
|
3111
|
+
{ method: "DELETE" },
|
|
3112
|
+
accessToken
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
listProjects(accessToken, delegationId) {
|
|
3116
|
+
return this.requestJson("/projects", { method: "GET" }, accessToken, void 0, delegationId);
|
|
3079
3117
|
}
|
|
3080
|
-
loadProject(projectId, accessToken) {
|
|
3081
|
-
return this.requestJson(`/projects/${encodeURIComponent(projectId)}`, { method: "GET" }, accessToken);
|
|
3118
|
+
loadProject(projectId, accessToken, delegationId) {
|
|
3119
|
+
return this.requestJson(`/projects/${encodeURIComponent(projectId)}`, { method: "GET" }, accessToken, void 0, delegationId);
|
|
3082
3120
|
}
|
|
3083
|
-
loadBoard(projectId, accessToken) {
|
|
3084
|
-
return this.requestJson(`/projects/${encodeURIComponent(projectId)}/board`, { method: "GET" }, accessToken);
|
|
3121
|
+
loadBoard(projectId, accessToken, delegationId) {
|
|
3122
|
+
return this.requestJson(`/projects/${encodeURIComponent(projectId)}/board`, { method: "GET" }, accessToken, void 0, delegationId);
|
|
3085
3123
|
}
|
|
3086
|
-
upsertTicketPacketLink(projectId, ticketId, request, accessToken) {
|
|
3124
|
+
upsertTicketPacketLink(projectId, ticketId, request, accessToken, delegationId) {
|
|
3087
3125
|
return this.requestJson(
|
|
3088
3126
|
`/projects/${encodeURIComponent(projectId)}/tickets/${encodeURIComponent(ticketId)}/packet-link`,
|
|
3089
3127
|
{
|
|
3090
3128
|
method: "PUT",
|
|
3091
3129
|
body: JSON.stringify(request)
|
|
3092
3130
|
},
|
|
3093
|
-
accessToken
|
|
3131
|
+
accessToken,
|
|
3132
|
+
void 0,
|
|
3133
|
+
delegationId
|
|
3094
3134
|
);
|
|
3095
3135
|
}
|
|
3096
|
-
moveTicket(projectId, ticketId, request, accessToken) {
|
|
3136
|
+
moveTicket(projectId, ticketId, request, accessToken, delegationId) {
|
|
3097
3137
|
return this.requestJson(
|
|
3098
3138
|
`/projects/${encodeURIComponent(projectId)}/tickets/${encodeURIComponent(ticketId)}/move`,
|
|
3099
3139
|
{
|
|
3100
3140
|
method: "POST",
|
|
3101
3141
|
body: JSON.stringify(request)
|
|
3102
3142
|
},
|
|
3103
|
-
accessToken
|
|
3143
|
+
accessToken,
|
|
3144
|
+
void 0,
|
|
3145
|
+
delegationId
|
|
3104
3146
|
);
|
|
3105
3147
|
}
|
|
3106
|
-
updateTicketNotes(projectId, ticketId, request, accessToken) {
|
|
3148
|
+
updateTicketNotes(projectId, ticketId, request, accessToken, delegationId) {
|
|
3107
3149
|
return this.requestJson(
|
|
3108
3150
|
`/projects/${encodeURIComponent(projectId)}/tickets/${encodeURIComponent(ticketId)}/notes`,
|
|
3109
3151
|
{
|
|
3110
3152
|
method: "PATCH",
|
|
3111
3153
|
body: JSON.stringify(request)
|
|
3112
3154
|
},
|
|
3113
|
-
accessToken
|
|
3155
|
+
accessToken,
|
|
3156
|
+
void 0,
|
|
3157
|
+
delegationId
|
|
3114
3158
|
);
|
|
3115
3159
|
}
|
|
3116
|
-
updateChecklistItem(projectId, ticketId, itemId, request, accessToken) {
|
|
3160
|
+
updateChecklistItem(projectId, ticketId, itemId, request, accessToken, delegationId) {
|
|
3117
3161
|
return this.requestJson(
|
|
3118
3162
|
`/projects/${encodeURIComponent(projectId)}/tickets/${encodeURIComponent(ticketId)}/checklist-items/${encodeURIComponent(itemId)}`,
|
|
3119
3163
|
{
|
|
3120
3164
|
method: "PATCH",
|
|
3121
3165
|
body: JSON.stringify(request)
|
|
3122
3166
|
},
|
|
3123
|
-
accessToken
|
|
3167
|
+
accessToken,
|
|
3168
|
+
void 0,
|
|
3169
|
+
delegationId
|
|
3124
3170
|
);
|
|
3125
3171
|
}
|
|
3126
|
-
async requestJson(path6, init, accessToken, refreshToken) {
|
|
3172
|
+
async requestJson(path6, init, accessToken, refreshToken, delegationId) {
|
|
3127
3173
|
const url = `${this.apiBaseUrl}${path6.startsWith("/") ? path6 : `/${path6}`}`;
|
|
3128
3174
|
const method = (init.method ?? "GET").toUpperCase();
|
|
3129
3175
|
const headers = new Headers(init.headers);
|
|
@@ -3140,6 +3186,9 @@ var SwarmCraftClient = class {
|
|
|
3140
3186
|
if (refreshToken) {
|
|
3141
3187
|
headers.set("Cookie", `${REFRESH_TOKEN_COOKIE_NAME}=${refreshToken}`);
|
|
3142
3188
|
}
|
|
3189
|
+
if (delegationId) {
|
|
3190
|
+
headers.set(DELEGATION_HEADER, delegationId);
|
|
3191
|
+
}
|
|
3143
3192
|
let response;
|
|
3144
3193
|
try {
|
|
3145
3194
|
response = await fetch(url, {
|
|
@@ -3218,6 +3267,7 @@ var import_packet_workflow = __toESM(require_dist(), 1);
|
|
|
3218
3267
|
var import_repo_seed = __toESM(require_dist2(), 1);
|
|
3219
3268
|
|
|
3220
3269
|
// src/workspace.ts
|
|
3270
|
+
var import_node_fs = require("node:fs");
|
|
3221
3271
|
var import_promises = require("node:fs/promises");
|
|
3222
3272
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
3223
3273
|
var import_node_child_process = require("node:child_process");
|
|
@@ -3259,7 +3309,11 @@ async function writeWorkspaceFile(workspacePath, relativePath, contents) {
|
|
|
3259
3309
|
await (0, import_promises.mkdir)(import_node_path.default.dirname(absolutePath), { recursive: true });
|
|
3260
3310
|
await (0, import_promises.writeFile)(absolutePath, contents, "utf8");
|
|
3261
3311
|
}
|
|
3262
|
-
async function runCommand(command, cwd, env = process.env) {
|
|
3312
|
+
async function runCommand(command, cwd, env = process.env, options = {}) {
|
|
3313
|
+
if (options.logPath) {
|
|
3314
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(options.logPath), { recursive: true });
|
|
3315
|
+
}
|
|
3316
|
+
const logStream = options.logPath ? (0, import_node_fs.createWriteStream)(options.logPath, { flags: "a" }) : null;
|
|
3263
3317
|
return new Promise((resolve) => {
|
|
3264
3318
|
const child = (0, import_node_child_process.spawn)(command, {
|
|
3265
3319
|
cwd,
|
|
@@ -3269,14 +3323,40 @@ async function runCommand(command, cwd, env = process.env) {
|
|
|
3269
3323
|
});
|
|
3270
3324
|
const stdoutChunks = [];
|
|
3271
3325
|
const stderrChunks = [];
|
|
3272
|
-
child.stdout.on("data", (chunk) =>
|
|
3273
|
-
|
|
3326
|
+
child.stdout.on("data", (chunk) => {
|
|
3327
|
+
stdoutChunks.push(chunk);
|
|
3328
|
+
logStream?.write(chunk);
|
|
3329
|
+
if (options.teeOutput) {
|
|
3330
|
+
process.stdout.write(chunk);
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
child.stderr.on("data", (chunk) => {
|
|
3334
|
+
stderrChunks.push(chunk);
|
|
3335
|
+
logStream?.write(chunk);
|
|
3336
|
+
if (options.teeOutput) {
|
|
3337
|
+
process.stderr.write(chunk);
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
child.on("error", (error) => {
|
|
3341
|
+
const chunk = Buffer.from(`${error.message}
|
|
3342
|
+
`, "utf8");
|
|
3343
|
+
stderrChunks.push(chunk);
|
|
3344
|
+
logStream?.write(chunk);
|
|
3345
|
+
if (options.teeOutput) {
|
|
3346
|
+
process.stderr.write(chunk);
|
|
3347
|
+
}
|
|
3348
|
+
});
|
|
3274
3349
|
child.on("close", (code) => {
|
|
3275
|
-
|
|
3350
|
+
const result = {
|
|
3276
3351
|
exitCode: code ?? 1,
|
|
3277
3352
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
3278
3353
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
3279
|
-
}
|
|
3354
|
+
};
|
|
3355
|
+
if (logStream) {
|
|
3356
|
+
logStream.end(() => resolve(result));
|
|
3357
|
+
return;
|
|
3358
|
+
}
|
|
3359
|
+
resolve(result);
|
|
3280
3360
|
});
|
|
3281
3361
|
});
|
|
3282
3362
|
}
|
|
@@ -3301,11 +3381,11 @@ async function runGit(args, cwd) {
|
|
|
3301
3381
|
// src/bootstrap.ts
|
|
3302
3382
|
async function runBootstrap(client, accessToken, options) {
|
|
3303
3383
|
const workspace = await validateWorkspace(options.workspacePath);
|
|
3304
|
-
const project = await client.loadProject(options.projectId, accessToken);
|
|
3384
|
+
const project = await client.loadProject(options.projectId, accessToken, options.delegationId);
|
|
3305
3385
|
if (project.boardState !== "UNLOCKED") {
|
|
3306
3386
|
throw new Error(`Project ${project.id} is ${project.boardState}; unlock the board before bootstrapping a workspace.`);
|
|
3307
3387
|
}
|
|
3308
|
-
const board = await client.loadBoard(project.id, accessToken);
|
|
3388
|
+
const board = await client.loadBoard(project.id, accessToken, options.delegationId);
|
|
3309
3389
|
const seedFilesWritten = await seedWorkspace(project, workspace.workspacePath);
|
|
3310
3390
|
const selectedTickets = selectBootstrapTickets(board, options);
|
|
3311
3391
|
const packetFilesCreated = [];
|
|
@@ -3355,14 +3435,17 @@ async function runBootstrap(client, accessToken, options) {
|
|
|
3355
3435
|
await writeWorkspaceFile(workspace.workspacePath, relativePacketPath, packetContents);
|
|
3356
3436
|
packetFilesCreated.push(relativePacketPath);
|
|
3357
3437
|
}
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3438
|
+
const packetLinkMatches = ticket.packetLink && ticket.packetLink.workspaceFolder === workspace.workspaceFolderName && ticket.packetLink.packetFilePath === relativePacketPath;
|
|
3439
|
+
if (!packetLinkMatches) {
|
|
3440
|
+
await client.upsertTicketPacketLink(project.id, ticket.id, {
|
|
3441
|
+
workspaceFolder: workspace.workspaceFolderName,
|
|
3442
|
+
packetFilePath: relativePacketPath,
|
|
3443
|
+
expectedRevision: ticket.revision,
|
|
3444
|
+
lastSyncedTicketRevision: ticket.revision,
|
|
3445
|
+
lastSyncedAt: syncedAt
|
|
3446
|
+
}, accessToken, options.delegationId);
|
|
3447
|
+
linkedPackets.push(relativePacketPath);
|
|
3448
|
+
}
|
|
3366
3449
|
}
|
|
3367
3450
|
return {
|
|
3368
3451
|
projectTitle: project.title || project.projectTitle || project.id,
|
|
@@ -3374,7 +3457,7 @@ async function runBootstrap(client, accessToken, options) {
|
|
|
3374
3457
|
linkedPackets,
|
|
3375
3458
|
skippedTickets,
|
|
3376
3459
|
nextCommands: [
|
|
3377
|
-
`swarmcraft one-shot --project ${project.id} --workspace ${workspace.workspacePath} --agent-provider codex`,
|
|
3460
|
+
`swarmcraft one-shot --project ${project.id} --workspace ${workspace.workspacePath} --agent-provider codex${options.delegationId ? ` --delegation ${options.delegationId}` : ""}`,
|
|
3378
3461
|
"Review generated files under .swarmcraft/, .github/, .agents/, and .codex/ before running agents."
|
|
3379
3462
|
]
|
|
3380
3463
|
};
|
|
@@ -3410,21 +3493,28 @@ async function seedWorkspace(project, workspacePath) {
|
|
|
3410
3493
|
var import_promises2 = require("node:fs/promises");
|
|
3411
3494
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
3412
3495
|
var import_packet_workflow2 = __toESM(require_dist(), 1);
|
|
3413
|
-
async function runOneShot(client,
|
|
3496
|
+
async function runOneShot(client, accessTokenProvider, options) {
|
|
3414
3497
|
const workspace = await validateWorkspace(options.workspacePath);
|
|
3498
|
+
const getAccessToken = () => resolveAccessToken(accessTokenProvider);
|
|
3415
3499
|
if (workspace.dirtyFileCount > 0 && !options.allowDirty) {
|
|
3416
3500
|
throw new Error(`Workspace has ${workspace.dirtyFileCount} changed file(s). Commit, stash, or rerun with --allow-dirty before one-shot.`);
|
|
3417
3501
|
}
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3502
|
+
if (options.resumeRunId) {
|
|
3503
|
+
printProgress("Resume detected; using existing packet files without re-running bootstrap.");
|
|
3504
|
+
} else {
|
|
3505
|
+
await runBootstrap(client, await getAccessToken(), {
|
|
3506
|
+
projectId: options.projectId,
|
|
3507
|
+
workspacePath: workspace.workspacePath,
|
|
3508
|
+
ticketIds: options.ticketIds,
|
|
3509
|
+
ticketLimit: options.ticketLimit,
|
|
3510
|
+
delegationId: options.delegation?.id ?? null
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
const project = await client.loadProject(options.projectId, await getAccessToken(), options.delegation?.id ?? null);
|
|
3514
|
+
let board = await client.loadBoard(project.id, await getAccessToken(), options.delegation?.id ?? null);
|
|
3426
3515
|
const runId = options.resumeRunId ?? options.runId ?? createRunId();
|
|
3427
3516
|
const manifestPath = resolveManifestPath(workspace.workspacePath, runId);
|
|
3517
|
+
const selectedTickets = selectTickets(board, options);
|
|
3428
3518
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3429
3519
|
const manifest = await loadOrCreateManifest(manifestPath, {
|
|
3430
3520
|
generator: "swarmcraft-cli",
|
|
@@ -3447,6 +3537,9 @@ async function runOneShot(client, accessToken, options) {
|
|
|
3447
3537
|
}
|
|
3448
3538
|
});
|
|
3449
3539
|
await persistManifest(manifestPath, manifest);
|
|
3540
|
+
printProgress(`Run ${runId} started for ${project.title || project.projectTitle || project.id}.`);
|
|
3541
|
+
printProgress(`Manifest: ${manifestPath}`);
|
|
3542
|
+
printProgress(`Selected tickets: ${selectedTickets.length}.`);
|
|
3450
3543
|
let agentRequests = manifest.tickets.flatMap((ticket) => ticket.stages).length;
|
|
3451
3544
|
const completedTicketIds = new Set(
|
|
3452
3545
|
manifest.tickets.filter((ticket) => ["done", "manual-review-required"].includes(ticket.outcome)).map((ticket) => ticket.ticketId)
|
|
@@ -3455,10 +3548,13 @@ async function runOneShot(client, accessToken, options) {
|
|
|
3455
3548
|
manifest.tickets = manifest.tickets.filter((ticket) => completedTicketIds.has(ticket.ticketId));
|
|
3456
3549
|
await persistManifest(manifestPath, manifest);
|
|
3457
3550
|
}
|
|
3458
|
-
|
|
3551
|
+
let finalManifestPersisted = false;
|
|
3552
|
+
for (const [ticketIndex, selectedTicket] of selectedTickets.entries()) {
|
|
3459
3553
|
if (completedTicketIds.has(selectedTicket.id)) {
|
|
3554
|
+
printProgress(`Skipping completed ticket ${ticketIndex + 1}/${selectedTickets.length}: ${selectedTicket.title}.`);
|
|
3460
3555
|
continue;
|
|
3461
3556
|
}
|
|
3557
|
+
printProgress(`Starting ticket ${ticketIndex + 1}/${selectedTickets.length}: ${selectedTicket.title}.`);
|
|
3462
3558
|
const ticketOutcome = {
|
|
3463
3559
|
ticketId: selectedTicket.id,
|
|
3464
3560
|
title: selectedTicket.title,
|
|
@@ -3473,7 +3569,7 @@ async function runOneShot(client, accessToken, options) {
|
|
|
3473
3569
|
await persistManifest(manifestPath, manifest);
|
|
3474
3570
|
const result = await processTicket({
|
|
3475
3571
|
client,
|
|
3476
|
-
|
|
3572
|
+
getAccessToken,
|
|
3477
3573
|
project,
|
|
3478
3574
|
board,
|
|
3479
3575
|
ticket: selectedTicket,
|
|
@@ -3491,30 +3587,59 @@ async function runOneShot(client, accessToken, options) {
|
|
|
3491
3587
|
ticketOutcome.outcome = result.outcome;
|
|
3492
3588
|
ticketOutcome.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3493
3589
|
ticketOutcome.elapsedMs = (0, import_packet_workflow2.computeElapsedMs)(ticketOutcome.startedAt, ticketOutcome.finishedAt);
|
|
3590
|
+
printProgress(`Ticket ${ticketIndex + 1}/${selectedTickets.length} finished as ${result.outcome} in ${formatDuration(ticketOutcome.elapsedMs)}.`);
|
|
3591
|
+
if (result.outcome === "done" || result.outcome === "manual-review-required") {
|
|
3592
|
+
completedTicketIds.add(selectedTicket.id);
|
|
3593
|
+
}
|
|
3594
|
+
const willStop = result.outcome.startsWith("blocked");
|
|
3595
|
+
const isFinalSelectedTicket = !hasRemainingUncompletedTickets(selectedTickets, ticketIndex + 1, completedTicketIds);
|
|
3494
3596
|
if (result.outcome === "done" && options.commitPolicy === "done") {
|
|
3495
|
-
ticketOutcome.commitCreated =
|
|
3597
|
+
ticketOutcome.commitCreated = true;
|
|
3598
|
+
}
|
|
3599
|
+
if (willStop || isFinalSelectedTicket) {
|
|
3600
|
+
finalizeManifest(manifest);
|
|
3601
|
+
finalManifestPersisted = true;
|
|
3496
3602
|
}
|
|
3497
3603
|
await persistManifest(manifestPath, manifest);
|
|
3498
|
-
if (result.outcome.
|
|
3604
|
+
if (result.outcome === "done" && options.commitPolicy === "done") {
|
|
3605
|
+
ticketOutcome.commitCreated = await gitAddAndCommit(workspace.workspacePath, `Complete ${selectedTicket.id}: ${selectedTicket.title}`);
|
|
3606
|
+
if (!ticketOutcome.commitCreated) {
|
|
3607
|
+
await persistManifest(manifestPath, manifest);
|
|
3608
|
+
}
|
|
3609
|
+
printProgress(ticketOutcome.commitCreated ? "Committed completed ticket changes." : "No completed ticket changes to commit.");
|
|
3610
|
+
}
|
|
3611
|
+
if (willStop) {
|
|
3612
|
+
printProgress(`Stopping because ticket is ${result.outcome}.`);
|
|
3499
3613
|
break;
|
|
3500
3614
|
}
|
|
3501
3615
|
}
|
|
3616
|
+
if (!finalManifestPersisted) {
|
|
3617
|
+
finalizeManifest(manifest);
|
|
3618
|
+
await persistManifest(manifestPath, manifest);
|
|
3619
|
+
}
|
|
3620
|
+
printProgress(`Run ${runId} finished with status ${manifest.status} in ${formatDuration(manifest.durations.elapsedMs)}.`);
|
|
3621
|
+
return manifest;
|
|
3622
|
+
}
|
|
3623
|
+
function hasRemainingUncompletedTickets(selectedTickets, startIndex, completedTicketIds) {
|
|
3624
|
+
return selectedTickets.slice(startIndex).some((ticket) => !completedTicketIds.has(ticket.id));
|
|
3625
|
+
}
|
|
3626
|
+
function finalizeManifest(manifest) {
|
|
3502
3627
|
manifest.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3503
3628
|
manifest.status = resolveManifestStatus(manifest);
|
|
3504
3629
|
manifest.durations = (0, import_packet_workflow2.summarizeWorkflowRunDurations)(manifest);
|
|
3505
|
-
await persistManifest(manifestPath, manifest);
|
|
3506
|
-
return manifest;
|
|
3507
3630
|
}
|
|
3508
3631
|
async function processTicket(context) {
|
|
3509
3632
|
let currentBoard = context.board;
|
|
3510
3633
|
let currentTicket = requireTicket(currentBoard, context.ticket.id);
|
|
3511
3634
|
if (currentTicket.workflowState === "todo") {
|
|
3512
|
-
|
|
3635
|
+
printProgress("Moving ticket to doing.");
|
|
3636
|
+
currentBoard = await moveTicket(context.client, await context.getAccessToken(), context.project.id, currentTicket, "doing", context.options.delegation?.id ?? null);
|
|
3513
3637
|
currentTicket = requireTicket(currentBoard, context.ticket.id);
|
|
3514
3638
|
}
|
|
3515
3639
|
const packetPath = (0, import_packet_workflow2.resolvePacketFilePath)(context.project, currentTicket, currentTicket.packetLink);
|
|
3516
3640
|
context.ticketOutcome.packetPath = packetPath;
|
|
3517
3641
|
await refreshPacketMetadata(context.workspacePath, packetPath, currentTicket);
|
|
3642
|
+
printProgress(`Packet: ${packetPath}`);
|
|
3518
3643
|
if (currentTicket.workflowState !== "checking" && currentTicket.workflowState !== "reviewing") {
|
|
3519
3644
|
const doResult = await runStageAndSync({ ...context, board: currentBoard, ticket: currentTicket, packetPath, stage: "do" });
|
|
3520
3645
|
currentBoard = doResult.board;
|
|
@@ -3524,7 +3649,8 @@ async function processTicket(context) {
|
|
|
3524
3649
|
return moveAfterAcceptedChecking(context, currentBoard, currentTicket, packetPath);
|
|
3525
3650
|
}
|
|
3526
3651
|
if (currentTicket.workflowState === "doing") {
|
|
3527
|
-
|
|
3652
|
+
printProgress("Moving ticket to checking.");
|
|
3653
|
+
currentBoard = await moveTicket(context.client, await context.getAccessToken(), context.project.id, currentTicket, "checking", context.options.delegation?.id ?? null);
|
|
3528
3654
|
currentTicket = requireTicket(currentBoard, currentTicket.id);
|
|
3529
3655
|
await refreshPacketMetadata(context.workspacePath, packetPath, currentTicket);
|
|
3530
3656
|
}
|
|
@@ -3534,9 +3660,11 @@ async function processTicket(context) {
|
|
|
3534
3660
|
currentTicket = checkResult.ticket;
|
|
3535
3661
|
if (currentTicket.workflowState === "doing") {
|
|
3536
3662
|
if (attempt >= context.options.maxCheckingAttempts) {
|
|
3663
|
+
printProgress("Checking sent the ticket back to doing and retry budget is exhausted.");
|
|
3537
3664
|
return { board: currentBoard, ticket: currentTicket, outcome: "blocked-checking-retries-exhausted" };
|
|
3538
3665
|
}
|
|
3539
|
-
|
|
3666
|
+
printProgress(`Checking requested more work; retrying checking attempt ${attempt + 1}/${context.options.maxCheckingAttempts}.`);
|
|
3667
|
+
currentBoard = await moveTicket(context.client, await context.getAccessToken(), context.project.id, currentTicket, "checking", context.options.delegation?.id ?? null);
|
|
3540
3668
|
currentTicket = requireTicket(currentBoard, currentTicket.id);
|
|
3541
3669
|
await refreshPacketMetadata(context.workspacePath, packetPath, currentTicket);
|
|
3542
3670
|
continue;
|
|
@@ -3566,9 +3694,12 @@ async function runStageAndSync(context) {
|
|
|
3566
3694
|
ticket: context.packetPath,
|
|
3567
3695
|
workspace: context.workspacePath
|
|
3568
3696
|
});
|
|
3697
|
+
const agentRequestNumber = context.getAgentRequests() + 1;
|
|
3698
|
+
const logPath = import_node_path3.default.join(import_node_path3.default.dirname(context.manifestPath), "logs", `${String(agentRequestNumber).padStart(4, "0")}-${context.stage}.log`);
|
|
3569
3699
|
const stageOutcome = {
|
|
3570
3700
|
stage: context.stage,
|
|
3571
3701
|
command,
|
|
3702
|
+
logPath: import_node_path3.default.relative(context.workspacePath, logPath),
|
|
3572
3703
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3573
3704
|
finishedAt: null,
|
|
3574
3705
|
elapsedMs: null,
|
|
@@ -3582,7 +3713,9 @@ async function runStageAndSync(context) {
|
|
|
3582
3713
|
context.ticketOutcome.stages.push(stageOutcome);
|
|
3583
3714
|
await persistManifest(context.manifestPath, context.manifest);
|
|
3584
3715
|
context.incrementAgentRequests();
|
|
3585
|
-
|
|
3716
|
+
printProgress(`Running ${context.stage} stage for ${context.ticket.title}.`);
|
|
3717
|
+
printProgress(`Agent log: ${stageOutcome.logPath}`);
|
|
3718
|
+
const costOutputPath = import_node_path3.default.join(import_node_path3.default.dirname(context.manifestPath), "agent-cost", `${String(agentRequestNumber).padStart(4, "0")}-${context.stage}.json`);
|
|
3586
3719
|
await (0, import_promises2.mkdir)(import_node_path3.default.dirname(costOutputPath), { recursive: true });
|
|
3587
3720
|
const execution = await runCommand(command, context.workspacePath, await buildAgentEnvironment(context.options, {
|
|
3588
3721
|
SWARMCRAFT_STAGE: context.stage,
|
|
@@ -3590,23 +3723,26 @@ async function runStageAndSync(context) {
|
|
|
3590
3723
|
SWARMCRAFT_WORKSPACE: context.workspacePath,
|
|
3591
3724
|
SWARMCRAFT_AGENT_PROVIDER: context.options.agentProvider,
|
|
3592
3725
|
SWARMCRAFT_AGENT_COST_OUTPUT: costOutputPath
|
|
3593
|
-
}));
|
|
3726
|
+
}), { logPath, teeOutput: true });
|
|
3594
3727
|
stageOutcome.exitCode = execution.exitCode;
|
|
3595
3728
|
if (execution.exitCode !== 0) {
|
|
3596
3729
|
stageOutcome.result = "failed";
|
|
3597
3730
|
stageOutcome.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3598
3731
|
stageOutcome.elapsedMs = (0, import_packet_workflow2.computeElapsedMs)(stageOutcome.startedAt, stageOutcome.finishedAt);
|
|
3599
3732
|
await persistManifest(context.manifestPath, context.manifest);
|
|
3733
|
+
printProgress(`${context.stage} stage failed in ${formatDuration(stageOutcome.elapsedMs)}.`);
|
|
3600
3734
|
throw new Error(`Agent command failed during ${context.stage}: ${execution.stderr || execution.stdout || command}`);
|
|
3601
3735
|
}
|
|
3602
3736
|
const syncResult = await syncPacketFile({
|
|
3603
3737
|
client: context.client,
|
|
3604
|
-
|
|
3738
|
+
getAccessToken: context.getAccessToken,
|
|
3605
3739
|
project: context.project,
|
|
3606
3740
|
board: context.board,
|
|
3607
3741
|
ticket: context.ticket,
|
|
3608
3742
|
workspacePath: context.workspacePath,
|
|
3609
|
-
relativePacketPath: context.packetPath
|
|
3743
|
+
relativePacketPath: context.packetPath,
|
|
3744
|
+
allowLaneSync: context.options.checking || context.stage !== "do",
|
|
3745
|
+
delegationId: context.options.delegation?.id ?? null
|
|
3610
3746
|
});
|
|
3611
3747
|
stageOutcome.result = "synced";
|
|
3612
3748
|
stageOutcome.costUsd = await readStageCostUsd(costOutputPath);
|
|
@@ -3622,6 +3758,7 @@ async function runStageAndSync(context) {
|
|
|
3622
3758
|
stageOutcome.laneAfter = syncResult.ticket.workflowState;
|
|
3623
3759
|
stageOutcome.syncedChanges = syncResult.syncedChanges;
|
|
3624
3760
|
await persistManifest(context.manifestPath, context.manifest);
|
|
3761
|
+
printProgress(`${context.stage} stage completed in ${formatDuration(stageOutcome.elapsedMs)}; synced ${syncResult.syncedChanges} change(s); lane is ${syncResult.ticket.workflowState}.`);
|
|
3625
3762
|
return { board: syncResult.board, ticket: syncResult.ticket };
|
|
3626
3763
|
}
|
|
3627
3764
|
async function syncPacketFile(input2) {
|
|
@@ -3652,7 +3789,7 @@ async function syncPacketFile(input2) {
|
|
|
3652
3789
|
currentBoard = await input2.client.updateChecklistItem(input2.project.id, currentTicket.id, currentItem.id, {
|
|
3653
3790
|
completed: parsedItem.completed,
|
|
3654
3791
|
expectedRevision: currentItem.revision
|
|
3655
|
-
}, input2.
|
|
3792
|
+
}, await input2.getAccessToken(), input2.delegationId);
|
|
3656
3793
|
currentTicket = requireTicket(currentBoard, currentTicket.id);
|
|
3657
3794
|
syncedChanges += 1;
|
|
3658
3795
|
}
|
|
@@ -3662,12 +3799,12 @@ async function syncPacketFile(input2) {
|
|
|
3662
3799
|
currentBoard = await input2.client.updateTicketNotes(input2.project.id, currentTicket.id, {
|
|
3663
3800
|
notes: desiredNotes,
|
|
3664
3801
|
expectedRevision: currentTicket.revision
|
|
3665
|
-
}, input2.
|
|
3802
|
+
}, await input2.getAccessToken(), input2.delegationId);
|
|
3666
3803
|
currentTicket = requireTicket(currentBoard, currentTicket.id);
|
|
3667
3804
|
syncedChanges += 1;
|
|
3668
3805
|
}
|
|
3669
|
-
if (parsedPacket.frontmatter.lane !== currentTicket.workflowState) {
|
|
3670
|
-
currentBoard = await moveTicket(input2.client, input2.
|
|
3806
|
+
if (input2.allowLaneSync && parsedPacket.frontmatter.lane !== currentTicket.workflowState) {
|
|
3807
|
+
currentBoard = await moveTicket(input2.client, await input2.getAccessToken(), input2.project.id, currentTicket, parsedPacket.frontmatter.lane, input2.delegationId);
|
|
3671
3808
|
currentTicket = requireTicket(currentBoard, currentTicket.id);
|
|
3672
3809
|
syncedChanges += 1;
|
|
3673
3810
|
}
|
|
@@ -3682,6 +3819,10 @@ async function buildAgentEnvironment(options, stageEnv) {
|
|
|
3682
3819
|
TERM: process.env.TERM,
|
|
3683
3820
|
...stageEnv
|
|
3684
3821
|
};
|
|
3822
|
+
if (options.commitPolicy === "done") {
|
|
3823
|
+
env.SWARMCRAFT_DRIVER_AUTO_COMMIT = "true";
|
|
3824
|
+
env.SWARMCRAFT_CLI_AUTO_COMMIT = "true";
|
|
3825
|
+
}
|
|
3685
3826
|
if (!options.agentApiKeyEnvName) {
|
|
3686
3827
|
return env;
|
|
3687
3828
|
}
|
|
@@ -3718,23 +3859,28 @@ function sumStageCostUsd(manifest) {
|
|
|
3718
3859
|
return manifest.tickets.flatMap((ticket) => ticket.stages).reduce((total, stage) => total + Number(stage.costUsd ?? 0), 0);
|
|
3719
3860
|
}
|
|
3720
3861
|
async function moveAfterAcceptedChecking(context, board, ticket, packetPath) {
|
|
3721
|
-
if (context.options.
|
|
3722
|
-
|
|
3862
|
+
if (!context.options.reviewing) {
|
|
3863
|
+
printProgress("Moving accepted ticket to done.");
|
|
3864
|
+
const doneBoard = ticket.workflowState === "done" ? board : await moveTicket(context.client, await context.getAccessToken(), context.project.id, ticket, "done", context.options.delegation?.id ?? null);
|
|
3723
3865
|
const doneTicket = requireTicket(doneBoard, ticket.id);
|
|
3724
3866
|
await refreshPacketMetadata(context.workspacePath, packetPath, doneTicket);
|
|
3725
3867
|
return { board: doneBoard, ticket: doneTicket, outcome: "done" };
|
|
3726
3868
|
}
|
|
3727
|
-
|
|
3869
|
+
let reviewBoard = board;
|
|
3870
|
+
if (ticket.workflowState !== "reviewing") {
|
|
3871
|
+
printProgress("Moving accepted ticket to reviewing.");
|
|
3872
|
+
reviewBoard = await moveTicket(context.client, await context.getAccessToken(), context.project.id, ticket, "reviewing", context.options.delegation?.id ?? null);
|
|
3873
|
+
}
|
|
3728
3874
|
const reviewTicket = requireTicket(reviewBoard, ticket.id);
|
|
3729
3875
|
await refreshPacketMetadata(context.workspacePath, packetPath, reviewTicket);
|
|
3730
3876
|
return { board: reviewBoard, ticket: reviewTicket, outcome: "manual-review-required" };
|
|
3731
3877
|
}
|
|
3732
|
-
async function moveTicket(client, accessToken, projectId, ticket, workflowState) {
|
|
3878
|
+
async function moveTicket(client, accessToken, projectId, ticket, workflowState, delegationId) {
|
|
3733
3879
|
return client.moveTicket(projectId, ticket.id, {
|
|
3734
3880
|
workflowState,
|
|
3735
3881
|
targetIndex: null,
|
|
3736
3882
|
expectedRevision: ticket.revision
|
|
3737
|
-
}, accessToken);
|
|
3883
|
+
}, accessToken, delegationId);
|
|
3738
3884
|
}
|
|
3739
3885
|
async function refreshPacketMetadata(workspacePath, relativePacketPath, ticket, syncedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3740
3886
|
const contents = await readWorkspaceFile(workspacePath, relativePacketPath);
|
|
@@ -3825,6 +3971,29 @@ function describeAgentPolicy(options) {
|
|
|
3825
3971
|
})
|
|
3826
3972
|
};
|
|
3827
3973
|
}
|
|
3974
|
+
function printProgress(message) {
|
|
3975
|
+
console.log(`[one-shot] ${message}`);
|
|
3976
|
+
}
|
|
3977
|
+
async function resolveAccessToken(accessTokenProvider) {
|
|
3978
|
+
return typeof accessTokenProvider === "string" ? accessTokenProvider : accessTokenProvider();
|
|
3979
|
+
}
|
|
3980
|
+
function formatDuration(elapsedMs) {
|
|
3981
|
+
if (elapsedMs === null || elapsedMs === void 0) {
|
|
3982
|
+
return "unknown";
|
|
3983
|
+
}
|
|
3984
|
+
const seconds = Math.round(elapsedMs / 1e3);
|
|
3985
|
+
if (seconds < 60) {
|
|
3986
|
+
return `${seconds}s`;
|
|
3987
|
+
}
|
|
3988
|
+
const minutes = Math.floor(seconds / 60);
|
|
3989
|
+
const remainingSeconds = seconds % 60;
|
|
3990
|
+
if (minutes < 60) {
|
|
3991
|
+
return remainingSeconds === 0 ? `${minutes}m` : `${minutes}m ${remainingSeconds}s`;
|
|
3992
|
+
}
|
|
3993
|
+
const hours = Math.floor(minutes / 60);
|
|
3994
|
+
const remainingMinutes = minutes % 60;
|
|
3995
|
+
return remainingMinutes === 0 ? `${hours}h` : `${hours}h ${remainingMinutes}m`;
|
|
3996
|
+
}
|
|
3828
3997
|
|
|
3829
3998
|
// src/sessionStore.ts
|
|
3830
3999
|
var import_promises3 = require("node:fs/promises");
|
|
@@ -3865,10 +4034,52 @@ function sessionExpiresSoon(session) {
|
|
|
3865
4034
|
return expiresAt - Date.now() < 6e4;
|
|
3866
4035
|
}
|
|
3867
4036
|
|
|
4037
|
+
// src/supportBundle.ts
|
|
4038
|
+
function summarizeDelegationForBundle(value) {
|
|
4039
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || !("options" in value)) {
|
|
4040
|
+
return null;
|
|
4041
|
+
}
|
|
4042
|
+
const options = value.options;
|
|
4043
|
+
if (!options || typeof options !== "object" || Array.isArray(options) || !("delegation" in options)) {
|
|
4044
|
+
return null;
|
|
4045
|
+
}
|
|
4046
|
+
const delegation = options.delegation;
|
|
4047
|
+
if (!delegation || typeof delegation !== "object" || Array.isArray(delegation)) {
|
|
4048
|
+
return null;
|
|
4049
|
+
}
|
|
4050
|
+
const typedDelegation = delegation;
|
|
4051
|
+
return {
|
|
4052
|
+
id: typedDelegation.id ?? null,
|
|
4053
|
+
status: typedDelegation.status ?? null,
|
|
4054
|
+
expiresAt: typedDelegation.expiresAt ?? null,
|
|
4055
|
+
supportTicketId: typedDelegation.supportTicketId ?? null,
|
|
4056
|
+
hasActorEmail: Boolean(typedDelegation.actorEmail),
|
|
4057
|
+
hasTargetEmail: Boolean(typedDelegation.targetEmail),
|
|
4058
|
+
hasTargetUserId: Boolean(typedDelegation.targetUserId),
|
|
4059
|
+
hasReason: Boolean(typedDelegation.reason)
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
function redactSupportBundleValue(value) {
|
|
4063
|
+
if (Array.isArray(value)) {
|
|
4064
|
+
return value.map(redactSupportBundleValue);
|
|
4065
|
+
}
|
|
4066
|
+
if (!value || typeof value !== "object") {
|
|
4067
|
+
return value;
|
|
4068
|
+
}
|
|
4069
|
+
return Object.fromEntries(
|
|
4070
|
+
Object.entries(value).map(([key, entry]) => [
|
|
4071
|
+
key,
|
|
4072
|
+
/token|secret|password|api[-_]?key|credential|actorEmail|targetEmail|targetUserId|actorUserId|reason/i.test(key) ? "<redacted>" : redactSupportBundleValue(entry)
|
|
4073
|
+
])
|
|
4074
|
+
);
|
|
4075
|
+
}
|
|
4076
|
+
|
|
3868
4077
|
// src/main.ts
|
|
3869
4078
|
var VERSION = package_default.version;
|
|
3870
4079
|
var DEFAULT_API_BASE_URL = "https://swarmcraft.ai/api";
|
|
3871
4080
|
var AGENT_PROVIDER_USAGE = import_packet_workflow3.EXTERNAL_AGENT_PROVIDERS.join("|");
|
|
4081
|
+
var MIN_DELEGATION_EXPIRY_MINUTES = 5;
|
|
4082
|
+
var MAX_DELEGATION_EXPIRY_MINUTES = 600;
|
|
3872
4083
|
var COMMANDS = [
|
|
3873
4084
|
{
|
|
3874
4085
|
name: "auth login",
|
|
@@ -3876,45 +4087,57 @@ var COMMANDS = [
|
|
|
3876
4087
|
summary: "Sign in to SwarmCraft and store a local CLI session.",
|
|
3877
4088
|
implemented: true
|
|
3878
4089
|
},
|
|
4090
|
+
{
|
|
4091
|
+
name: "delegation start",
|
|
4092
|
+
usage: "swarmcraft delegation start --user <email-or-id> --reason <text> [--support-ticket <id>] [--expires-in-minutes <n>]",
|
|
4093
|
+
summary: "Start short-lived operator access to a customer project workspace.",
|
|
4094
|
+
implemented: true
|
|
4095
|
+
},
|
|
4096
|
+
{
|
|
4097
|
+
name: "delegation end",
|
|
4098
|
+
usage: "swarmcraft delegation end --delegation <id>",
|
|
4099
|
+
summary: "End an active operator delegation.",
|
|
4100
|
+
implemented: true
|
|
4101
|
+
},
|
|
3879
4102
|
{
|
|
3880
4103
|
name: "projects list",
|
|
3881
|
-
usage: "swarmcraft projects list",
|
|
4104
|
+
usage: "swarmcraft projects list [--delegation <id>] [--on-behalf-of <email-or-id> --delegation-reason <text>]",
|
|
3882
4105
|
summary: "List SwarmCraft projects available to the signed-in user.",
|
|
3883
4106
|
implemented: true
|
|
3884
4107
|
},
|
|
3885
4108
|
{
|
|
3886
4109
|
name: "init",
|
|
3887
|
-
usage: "swarmcraft init --project <project-id> --workspace <path> [--ticket <ticket-id>] [--ticket-limit <n|all>]",
|
|
4110
|
+
usage: "swarmcraft init --project <project-id> --workspace <path> [--ticket <ticket-id>] [--ticket-limit <n|all>] [--delegation <id>]",
|
|
3888
4111
|
summary: "Prepare a repository with SwarmCraft packet and agent guidance files.",
|
|
3889
4112
|
implemented: true
|
|
3890
4113
|
},
|
|
3891
4114
|
{
|
|
3892
4115
|
name: "one-shot",
|
|
3893
|
-
usage: `swarmcraft one-shot --project <project-id> --workspace <path> --agent-provider ${AGENT_PROVIDER_USAGE} [--run-id <id>] [--done-policy stop|auto] [--commit-policy none|done] [--allow-dirty]`,
|
|
4116
|
+
usage: `swarmcraft one-shot --project <project-id> --workspace <path> --agent-provider ${AGENT_PROVIDER_USAGE} [--delegation <id>] [--run-id <id>] [--checking on|off] [--reviewing on|off] [--done-policy stop|auto] [--commit-policy none|done] [--allow-dirty]`,
|
|
3894
4117
|
summary: "Run the project board packet loop with the selected agent provider.",
|
|
3895
4118
|
implemented: true
|
|
3896
4119
|
},
|
|
3897
4120
|
{
|
|
3898
4121
|
name: "resume",
|
|
3899
|
-
usage: "swarmcraft resume --run <run-id> --workspace <path>",
|
|
4122
|
+
usage: "swarmcraft resume --run <run-id> --workspace <path> [--allow-dirty] [--delegation <id>]",
|
|
3900
4123
|
summary: "Resume a prior CLI run from its recorded manifest.",
|
|
3901
4124
|
implemented: true
|
|
3902
4125
|
},
|
|
3903
4126
|
{
|
|
3904
4127
|
name: "status",
|
|
3905
|
-
usage: "swarmcraft status --run <run-id> --workspace <path>",
|
|
4128
|
+
usage: "swarmcraft status --run <run-id> --workspace <path> [--delegation <id>]",
|
|
3906
4129
|
summary: "Show the status of a recorded CLI run.",
|
|
3907
4130
|
implemented: true
|
|
3908
4131
|
},
|
|
3909
4132
|
{
|
|
3910
4133
|
name: "doctor",
|
|
3911
|
-
usage: "swarmcraft doctor [--workspace <path>] [--agent-provider codex|copilot|custom]",
|
|
4134
|
+
usage: "swarmcraft doctor [--workspace <path>] [--agent-provider codex|copilot|custom] [--delegation <id>]",
|
|
3912
4135
|
summary: "Check local auth, API, git, workspace, runtime, and agent prerequisites.",
|
|
3913
4136
|
implemented: true
|
|
3914
4137
|
},
|
|
3915
4138
|
{
|
|
3916
4139
|
name: "support-bundle",
|
|
3917
|
-
usage: "swarmcraft support-bundle --run <run-id> --workspace <path>",
|
|
4140
|
+
usage: "swarmcraft support-bundle --run <run-id> --workspace <path> [--delegation <id>]",
|
|
3918
4141
|
summary: "Collect a redacted support bundle for a CLI run.",
|
|
3919
4142
|
implemented: true
|
|
3920
4143
|
}
|
|
@@ -3951,6 +4174,12 @@ Run "swarmcraft --help" for available commands.`);
|
|
|
3951
4174
|
case "auth login":
|
|
3952
4175
|
await runAuthLogin(parsed);
|
|
3953
4176
|
return 0;
|
|
4177
|
+
case "delegation start":
|
|
4178
|
+
await runDelegationStart(parsed);
|
|
4179
|
+
return 0;
|
|
4180
|
+
case "delegation end":
|
|
4181
|
+
await runDelegationEnd(parsed);
|
|
4182
|
+
return 0;
|
|
3954
4183
|
case "projects list":
|
|
3955
4184
|
await runProjectsList(parsed);
|
|
3956
4185
|
return 0;
|
|
@@ -4001,9 +4230,23 @@ async function runAuthLogin(parsed) {
|
|
|
4001
4230
|
await writeStoredSession(toStoredSession(apiBaseUrl, tokenResponse));
|
|
4002
4231
|
console.log(`Signed in as ${tokenResponse.user.email}.`);
|
|
4003
4232
|
}
|
|
4004
|
-
async function
|
|
4233
|
+
async function runDelegationStart(parsed) {
|
|
4005
4234
|
const { client, session } = await resolveAuthenticatedClient(parsed);
|
|
4006
|
-
const
|
|
4235
|
+
const delegation = await client.startDelegation(session.accessToken, buildStartDelegationRequest(parsed, {
|
|
4236
|
+
userOption: "user",
|
|
4237
|
+
reasonOption: "reason"
|
|
4238
|
+
}));
|
|
4239
|
+
printDelegationLifecycle("Delegation started", toDelegationContext(delegation));
|
|
4240
|
+
}
|
|
4241
|
+
async function runDelegationEnd(parsed) {
|
|
4242
|
+
const { client, session } = await resolveAuthenticatedClient(parsed);
|
|
4243
|
+
const delegation = await client.endDelegation(session.accessToken, requireOption(parsed, "delegation"));
|
|
4244
|
+
printDelegationLifecycle("Delegation ended", toDelegationContext(delegation));
|
|
4245
|
+
}
|
|
4246
|
+
async function runProjectsList(parsed) {
|
|
4247
|
+
const { client, session, delegation } = await resolveProjectCommandContext(parsed);
|
|
4248
|
+
printDelegationContext(delegation);
|
|
4249
|
+
const projects = await client.listProjects(session.accessToken, delegation?.id ?? null);
|
|
4007
4250
|
if (projects.length === 0) {
|
|
4008
4251
|
console.log("No projects available.");
|
|
4009
4252
|
return;
|
|
@@ -4018,19 +4261,25 @@ async function runInit(parsed) {
|
|
|
4018
4261
|
const projectId = requireOption(parsed, "project");
|
|
4019
4262
|
const workspacePath = requireOption(parsed, "workspace");
|
|
4020
4263
|
const ticketLimit = parseTicketLimit(getOption(parsed, "ticket-limit") ?? "all");
|
|
4021
|
-
const { client, session } = await
|
|
4264
|
+
const { client, session, delegation } = await resolveProjectCommandContext(parsed);
|
|
4265
|
+
printDelegationContext(delegation);
|
|
4022
4266
|
const summary = await runBootstrap(client, session.accessToken, {
|
|
4023
4267
|
projectId,
|
|
4024
4268
|
workspacePath,
|
|
4025
4269
|
ticketIds: getOptions(parsed, "ticket"),
|
|
4026
|
-
ticketLimit
|
|
4270
|
+
ticketLimit,
|
|
4271
|
+
delegationId: delegation?.id ?? null
|
|
4027
4272
|
});
|
|
4028
4273
|
printBootstrapSummary(summary);
|
|
4029
4274
|
}
|
|
4030
4275
|
async function runOneShotCommand(parsed) {
|
|
4031
|
-
const { client, session } = await
|
|
4032
|
-
const options =
|
|
4276
|
+
const { client, session, delegation } = await resolveProjectCommandContext(parsed);
|
|
4277
|
+
const options = {
|
|
4278
|
+
...parseOneShotOptions(parsed),
|
|
4279
|
+
delegation
|
|
4280
|
+
};
|
|
4033
4281
|
const policy = describeAgentPolicy(options);
|
|
4282
|
+
printDelegationContext(delegation);
|
|
4034
4283
|
console.log(`Agent provider: ${options.agentProvider}`);
|
|
4035
4284
|
console.log(`Agent sandbox: ${policy.sandboxMode ?? "custom"}`);
|
|
4036
4285
|
console.log(`Agent approval posture: ${policy.approvalPosture ?? "custom"}`);
|
|
@@ -4040,7 +4289,7 @@ async function runOneShotCommand(parsed) {
|
|
|
4040
4289
|
};
|
|
4041
4290
|
process.once("SIGINT", onSigint);
|
|
4042
4291
|
try {
|
|
4043
|
-
const manifest = await runOneShot(client, session
|
|
4292
|
+
const manifest = await runOneShot(client, createAccessTokenProvider(client, session), options);
|
|
4044
4293
|
printRunSummary(manifest);
|
|
4045
4294
|
} finally {
|
|
4046
4295
|
process.off("SIGINT", onSigint);
|
|
@@ -4050,10 +4299,13 @@ async function runResume(parsed) {
|
|
|
4050
4299
|
const runId = requireOption(parsed, "run");
|
|
4051
4300
|
const workspacePath = requireOption(parsed, "workspace");
|
|
4052
4301
|
const manifest = await readRunManifest(workspacePath, runId);
|
|
4053
|
-
const { client, session } = await
|
|
4054
|
-
|
|
4302
|
+
const { client, session, delegation } = await resolveProjectCommandContext(parsed, manifest.options.delegation ?? null);
|
|
4303
|
+
printDelegationContext(delegation);
|
|
4304
|
+
const resumed = await runOneShot(client, createAccessTokenProvider(client, session), {
|
|
4055
4305
|
...manifest.options,
|
|
4306
|
+
delegation,
|
|
4056
4307
|
workspacePath,
|
|
4308
|
+
allowDirty: hasFlag(parsed, "allow-dirty") ? true : manifest.options.allowDirty,
|
|
4057
4309
|
resumeRunId: runId
|
|
4058
4310
|
});
|
|
4059
4311
|
printRunSummary(resumed);
|
|
@@ -4061,7 +4313,8 @@ async function runResume(parsed) {
|
|
|
4061
4313
|
async function runStatus(parsed) {
|
|
4062
4314
|
const runId = requireOption(parsed, "run");
|
|
4063
4315
|
const workspacePath = requireOption(parsed, "workspace");
|
|
4064
|
-
|
|
4316
|
+
const manifest = await readRunManifest(workspacePath, runId);
|
|
4317
|
+
printRunSummary(manifest);
|
|
4065
4318
|
}
|
|
4066
4319
|
async function runDoctor(parsed) {
|
|
4067
4320
|
console.log(`Node: ${process.version}`);
|
|
@@ -4069,8 +4322,9 @@ async function runDoctor(parsed) {
|
|
|
4069
4322
|
console.log(`Session: ${session ? `signed in as ${session.user.email}` : "missing"}`);
|
|
4070
4323
|
if (session) {
|
|
4071
4324
|
try {
|
|
4072
|
-
const client =
|
|
4073
|
-
await client.listProjects(
|
|
4325
|
+
const { client, session: resolvedSession, delegation } = await resolveProjectCommandContext(parsed);
|
|
4326
|
+
await client.listProjects(resolvedSession.accessToken, delegation?.id ?? null);
|
|
4327
|
+
printDelegationContext(delegation);
|
|
4074
4328
|
console.log("API: reachable");
|
|
4075
4329
|
} catch (error) {
|
|
4076
4330
|
console.log(`API: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -4101,13 +4355,14 @@ async function runSupportBundle(parsed) {
|
|
|
4101
4355
|
const manifest = JSON.parse(await (0, import_promises4.readFile)(manifestPath, "utf8"));
|
|
4102
4356
|
const bundleDir = import_node_path5.default.join(workspacePath, ".swarmcraft", "runs", runId, "support-bundle");
|
|
4103
4357
|
await (0, import_promises4.mkdir)(bundleDir, { recursive: true });
|
|
4104
|
-
await (0, import_promises4.writeFile)(import_node_path5.default.join(bundleDir, "manifest.redacted.json"), `${JSON.stringify(
|
|
4358
|
+
await (0, import_promises4.writeFile)(import_node_path5.default.join(bundleDir, "manifest.redacted.json"), `${JSON.stringify(redactSupportBundleValue(manifest), null, 2)}
|
|
4105
4359
|
`, "utf8");
|
|
4106
4360
|
await (0, import_promises4.writeFile)(import_node_path5.default.join(bundleDir, "summary.json"), `${JSON.stringify({
|
|
4107
4361
|
runId,
|
|
4108
4362
|
manifestPath,
|
|
4109
4363
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4110
|
-
node: process.version
|
|
4364
|
+
node: process.version,
|
|
4365
|
+
delegation: summarizeDelegationForBundle(manifest)
|
|
4111
4366
|
}, null, 2)}
|
|
4112
4367
|
`, "utf8");
|
|
4113
4368
|
console.log(`Support bundle written: ${bundleDir}`);
|
|
@@ -4119,6 +4374,10 @@ async function resolveAuthenticatedClient(parsed) {
|
|
|
4119
4374
|
}
|
|
4120
4375
|
const apiBaseUrl = getOption(parsed, "api-base-url") ?? process.env.SWARMCRAFT_API_BASE_URL ?? session.apiBaseUrl;
|
|
4121
4376
|
const client = new SwarmCraftClient(apiBaseUrl);
|
|
4377
|
+
session = {
|
|
4378
|
+
...session,
|
|
4379
|
+
apiBaseUrl
|
|
4380
|
+
};
|
|
4122
4381
|
if (sessionExpiresSoon(session)) {
|
|
4123
4382
|
if (!session.refreshToken) {
|
|
4124
4383
|
throw new Error("Stored SwarmCraft session has expired. Run `swarmcraft auth login` again.");
|
|
@@ -4132,6 +4391,79 @@ async function resolveAuthenticatedClient(parsed) {
|
|
|
4132
4391
|
}
|
|
4133
4392
|
return { client, session };
|
|
4134
4393
|
}
|
|
4394
|
+
function createAccessTokenProvider(client, initialSession) {
|
|
4395
|
+
let session = initialSession;
|
|
4396
|
+
let refreshPromise = null;
|
|
4397
|
+
return async () => {
|
|
4398
|
+
if (!sessionExpiresSoon(session)) {
|
|
4399
|
+
return session.accessToken;
|
|
4400
|
+
}
|
|
4401
|
+
if (!session.refreshToken) {
|
|
4402
|
+
throw new Error("Stored SwarmCraft session has expired. Run `swarmcraft auth login` again.");
|
|
4403
|
+
}
|
|
4404
|
+
refreshPromise ??= (async () => {
|
|
4405
|
+
const refreshed = await client.refresh(session.refreshToken);
|
|
4406
|
+
session = toStoredSession(session.apiBaseUrl, {
|
|
4407
|
+
...refreshed,
|
|
4408
|
+
refreshToken: refreshed.refreshToken ?? session.refreshToken
|
|
4409
|
+
});
|
|
4410
|
+
await writeStoredSession(session);
|
|
4411
|
+
})().finally(() => {
|
|
4412
|
+
refreshPromise = null;
|
|
4413
|
+
});
|
|
4414
|
+
await refreshPromise;
|
|
4415
|
+
return session.accessToken;
|
|
4416
|
+
};
|
|
4417
|
+
}
|
|
4418
|
+
async function resolveProjectCommandContext(parsed, fallbackDelegation = null) {
|
|
4419
|
+
const { client, session } = await resolveAuthenticatedClient(parsed);
|
|
4420
|
+
const delegation = await resolveDelegationContext(client, session, parsed, fallbackDelegation);
|
|
4421
|
+
return { client, session, delegation };
|
|
4422
|
+
}
|
|
4423
|
+
async function resolveDelegationContext(client, session, parsed, fallbackDelegation) {
|
|
4424
|
+
const delegationId = getOption(parsed, "delegation");
|
|
4425
|
+
const onBehalfOf = getOption(parsed, "on-behalf-of");
|
|
4426
|
+
if (delegationId && onBehalfOf) {
|
|
4427
|
+
throw new Error("Use either --delegation or --on-behalf-of, not both.");
|
|
4428
|
+
}
|
|
4429
|
+
if (delegationId) {
|
|
4430
|
+
return toDelegationContext(await client.loadDelegation(session.accessToken, delegationId));
|
|
4431
|
+
}
|
|
4432
|
+
if (onBehalfOf) {
|
|
4433
|
+
const delegation = await client.startDelegation(session.accessToken, buildStartDelegationRequest(parsed, {
|
|
4434
|
+
userOption: "on-behalf-of",
|
|
4435
|
+
reasonOption: "delegation-reason"
|
|
4436
|
+
}));
|
|
4437
|
+
const context = toDelegationContext(delegation);
|
|
4438
|
+
printDelegationLifecycle("Delegation started", context);
|
|
4439
|
+
return context;
|
|
4440
|
+
}
|
|
4441
|
+
if (hasFlag(parsed, "delegation-reason")) {
|
|
4442
|
+
throw new Error("--delegation-reason requires --on-behalf-of.");
|
|
4443
|
+
}
|
|
4444
|
+
return fallbackDelegation;
|
|
4445
|
+
}
|
|
4446
|
+
function buildStartDelegationRequest(parsed, options) {
|
|
4447
|
+
const expiresInMinutes = getOption(parsed, "expires-in-minutes") ? parseDelegationExpiry(getOption(parsed, "expires-in-minutes")) : null;
|
|
4448
|
+
return {
|
|
4449
|
+
user: requireOption(parsed, options.userOption),
|
|
4450
|
+
reason: requireOption(parsed, options.reasonOption),
|
|
4451
|
+
supportTicketId: getOption(parsed, "support-ticket") ?? null,
|
|
4452
|
+
expiresInMinutes
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
function toDelegationContext(delegation) {
|
|
4456
|
+
return {
|
|
4457
|
+
id: delegation.id,
|
|
4458
|
+
actorEmail: delegation.actorEmail,
|
|
4459
|
+
targetUserId: delegation.targetUserId,
|
|
4460
|
+
targetEmail: delegation.targetEmail,
|
|
4461
|
+
reason: delegation.reason,
|
|
4462
|
+
supportTicketId: delegation.supportTicketId,
|
|
4463
|
+
status: delegation.status,
|
|
4464
|
+
expiresAt: delegation.expiresAt
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4135
4467
|
function printBootstrapSummary(summary) {
|
|
4136
4468
|
console.log(`Bootstrapped ${summary.projectTitle}`);
|
|
4137
4469
|
console.log(`Workspace: ${summary.workspacePath}`);
|
|
@@ -4153,10 +4485,26 @@ function printBootstrapSummary(summary) {
|
|
|
4153
4485
|
console.log(` ${nextCommand}`);
|
|
4154
4486
|
}
|
|
4155
4487
|
}
|
|
4488
|
+
function printDelegationLifecycle(prefix, delegation) {
|
|
4489
|
+
console.log(`${prefix}: ${delegation.id}`);
|
|
4490
|
+
printDelegationContext(delegation);
|
|
4491
|
+
if (delegation.expiresAt) {
|
|
4492
|
+
console.log(`Delegation expires: ${delegation.expiresAt}`);
|
|
4493
|
+
}
|
|
4494
|
+
}
|
|
4495
|
+
function printDelegationContext(delegation) {
|
|
4496
|
+
if (!delegation) {
|
|
4497
|
+
return;
|
|
4498
|
+
}
|
|
4499
|
+
const actor = delegation.actorEmail ?? "operator";
|
|
4500
|
+
const target = delegation.targetEmail ?? delegation.targetUserId ?? "target user";
|
|
4501
|
+
console.log(`Delegated access: ${actor} -> ${target} (${delegation.id})`);
|
|
4502
|
+
}
|
|
4156
4503
|
function printRunSummary(manifest) {
|
|
4157
4504
|
console.log(`Run: ${manifest.runId}`);
|
|
4158
4505
|
console.log(`Status: ${manifest.status}`);
|
|
4159
4506
|
console.log(`Workspace: ${manifest.workspacePath}`);
|
|
4507
|
+
printDelegationContext(manifest.options?.delegation ?? null);
|
|
4160
4508
|
console.log(`Tickets: ${manifest.tickets.length}`);
|
|
4161
4509
|
for (const ticket of manifest.tickets) {
|
|
4162
4510
|
console.log(` - ${ticket.outcome}: ${ticket.title} (${ticket.stages.length} stage(s))`);
|
|
@@ -4167,14 +4515,16 @@ function printRunSummary(manifest) {
|
|
|
4167
4515
|
}
|
|
4168
4516
|
function parseOneShotOptions(parsed) {
|
|
4169
4517
|
const agentProvider = (0, import_packet_workflow3.parseExternalAgentProvider)(requireOption(parsed, "agent-provider"));
|
|
4170
|
-
const donePolicy = parseChoice(getOption(parsed, "done-policy") ?? "
|
|
4518
|
+
const donePolicy = parseChoice(getOption(parsed, "done-policy") ?? "auto", ["stop", "auto"], "--done-policy");
|
|
4171
4519
|
const commitPolicy = parseChoice(getOption(parsed, "commit-policy") ?? "none", ["none", "done"], "--commit-policy");
|
|
4520
|
+
const explicitReviewing = getOption(parsed, "reviewing");
|
|
4172
4521
|
return {
|
|
4173
4522
|
projectId: requireOption(parsed, "project"),
|
|
4174
4523
|
workspacePath: requireOption(parsed, "workspace"),
|
|
4175
4524
|
agentProvider,
|
|
4176
4525
|
agentCommand: getOption(parsed, "agent-command") ?? process.env.SWARMCRAFT_AGENT_COMMAND ?? null,
|
|
4177
|
-
checking: parseOnOff(getOption(parsed, "checking") ?? "
|
|
4526
|
+
checking: parseOnOff(getOption(parsed, "checking") ?? "off", "--checking"),
|
|
4527
|
+
reviewing: explicitReviewing ? parseOnOff(explicitReviewing, "--reviewing") : donePolicy === "stop",
|
|
4178
4528
|
maxCheckingAttempts: parsePositiveInteger(getOption(parsed, "max-checking-attempts") ?? "2", "--max-checking-attempts"),
|
|
4179
4529
|
donePolicy,
|
|
4180
4530
|
commitPolicy,
|
|
@@ -4186,6 +4536,7 @@ function parseOneShotOptions(parsed) {
|
|
|
4186
4536
|
agentApiKeyEnvName: getOption(parsed, "agent-api-key-env-name") ?? null,
|
|
4187
4537
|
ticketIds: getOptions(parsed, "ticket"),
|
|
4188
4538
|
ticketLimit: parseTicketLimit(getOption(parsed, "ticket-limit") ?? "all"),
|
|
4539
|
+
delegation: null,
|
|
4189
4540
|
runId: parseRunId(getOption(parsed, "run-id"))
|
|
4190
4541
|
};
|
|
4191
4542
|
}
|
|
@@ -4221,7 +4572,7 @@ function findCommand(positionals) {
|
|
|
4221
4572
|
if (positionals.length === 0) {
|
|
4222
4573
|
return void 0;
|
|
4223
4574
|
}
|
|
4224
|
-
const commandKey = positionals[0] === "auth" || positionals[0] === "projects" ? positionals.slice(0, 2).join(" ") : positionals[0];
|
|
4575
|
+
const commandKey = positionals[0] === "auth" || positionals[0] === "projects" || positionals[0] === "delegation" ? positionals.slice(0, 2).join(" ") : positionals[0];
|
|
4225
4576
|
return COMMANDS.find((command) => command.name === commandKey);
|
|
4226
4577
|
}
|
|
4227
4578
|
function printGeneralHelp() {
|
|
@@ -4286,6 +4637,13 @@ function parsePositiveInteger(rawValue, flagName) {
|
|
|
4286
4637
|
}
|
|
4287
4638
|
return parsed;
|
|
4288
4639
|
}
|
|
4640
|
+
function parseDelegationExpiry(rawValue) {
|
|
4641
|
+
const parsed = parsePositiveInteger(rawValue, "--expires-in-minutes");
|
|
4642
|
+
if (parsed < MIN_DELEGATION_EXPIRY_MINUTES || parsed > MAX_DELEGATION_EXPIRY_MINUTES) {
|
|
4643
|
+
throw new Error(`--expires-in-minutes must be between ${MIN_DELEGATION_EXPIRY_MINUTES} and ${MAX_DELEGATION_EXPIRY_MINUTES}.`);
|
|
4644
|
+
}
|
|
4645
|
+
return parsed;
|
|
4646
|
+
}
|
|
4289
4647
|
function parseNonNegativeNumber(rawValue, flagName) {
|
|
4290
4648
|
const parsed = Number.parseFloat(rawValue);
|
|
4291
4649
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -4317,20 +4675,6 @@ function parseRunId(rawValue) {
|
|
|
4317
4675
|
}
|
|
4318
4676
|
return rawValue;
|
|
4319
4677
|
}
|
|
4320
|
-
function redact(value) {
|
|
4321
|
-
if (Array.isArray(value)) {
|
|
4322
|
-
return value.map(redact);
|
|
4323
|
-
}
|
|
4324
|
-
if (!value || typeof value !== "object") {
|
|
4325
|
-
return value;
|
|
4326
|
-
}
|
|
4327
|
-
return Object.fromEntries(
|
|
4328
|
-
Object.entries(value).map(([key, entry]) => [
|
|
4329
|
-
key,
|
|
4330
|
-
/token|secret|password|api[-_]?key|credential/i.test(key) ? "<redacted>" : redact(entry)
|
|
4331
|
-
])
|
|
4332
|
-
);
|
|
4333
|
-
}
|
|
4334
4678
|
async function promptLine(prompt) {
|
|
4335
4679
|
const readline = (0, import_promises5.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
4336
4680
|
try {
|
|
@@ -4342,14 +4686,26 @@ async function promptLine(prompt) {
|
|
|
4342
4686
|
async function promptSecret(prompt) {
|
|
4343
4687
|
return promptLine(prompt);
|
|
4344
4688
|
}
|
|
4345
|
-
|
|
4346
|
-
process.exitCode
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4689
|
+
if (isCliEntrypoint()) {
|
|
4690
|
+
main(process.argv.slice(2)).then((exitCode) => {
|
|
4691
|
+
process.exitCode = exitCode;
|
|
4692
|
+
}).catch((error) => {
|
|
4693
|
+
if (error instanceof SwarmCraftApiError) {
|
|
4694
|
+
const request = error.requestId ? ` requestId=${error.requestId}` : "";
|
|
4695
|
+
console.error(`${error.code}: ${error.message}${request}`);
|
|
4696
|
+
} else {
|
|
4697
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4698
|
+
}
|
|
4699
|
+
process.exitCode = 1;
|
|
4700
|
+
});
|
|
4701
|
+
}
|
|
4702
|
+
function isCliEntrypoint() {
|
|
4703
|
+
const invokedScript = process.argv[1] ? import_node_path5.default.basename(process.argv[1]) : "";
|
|
4704
|
+
return invokedScript === "main.cjs" || invokedScript === "main.js" || invokedScript === "main.ts";
|
|
4705
|
+
}
|
|
4706
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4707
|
+
0 && (module.exports = {
|
|
4708
|
+
findCommand,
|
|
4709
|
+
main,
|
|
4710
|
+
parseArgs
|
|
4355
4711
|
});
|
package/package.json
CHANGED