@productlobster/plob 0.1.4 → 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/dist/cli.js +229 -76
- package/dist/index.d.ts +15 -2
- package/dist/index.js +229 -76
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -24,10 +24,10 @@ A full analysis is asynchronous and durable. The loop is the same for every work
|
|
|
24
24
|
|
|
25
25
|
1. **Start.** Call \`run-start\` with a \`workflow\` of \`launch\`, \`audit\`, \`focused-analysis\`, \`prototype\`, or \`refinement\`, plus its inputs. It returns a run id. For \`prototype\`, the returned id is a prototype id, not a run id: track it with \`prototype-list\` and fetch it with \`prototype-pack\`, not \`run-status\`. If your request is too thin, it returns a \`needs_sharpening\` result that lists what to add \u2014 add detail and call \`run-start\` again.
|
|
26
26
|
2. **Poll.** Call \`run-status\` with the run id. It reports the current status and returns a \`since\` cursor; pass that cursor on the next poll to get only what changed. Runs take minutes \u2014 poll, do not block. Stop polling when the run's \`status\` is \`completed\`, \`failed\`, or \`cancelled\`. Do not wait for every stage to leave \`pending\`: a run that fails or is cancelled part-way leaves its unstarted stages \`pending\` permanently, and on a finished run \`pending\` means the stage never ran.
|
|
27
|
-
3. **Resolve checkpoints.** When \`run-status\` reports that the run is waiting at a checkpoint, it lists the options. Pick one and call \`checkpoint-resolve\` with the run id and an \`optionId\` taken from that list. An invented or stale id is rejected.
|
|
27
|
+
3. **Resolve checkpoints.** When \`run-status\` reports that the run is waiting at a checkpoint, it lists the options. Pick one and call \`checkpoint-resolve\` with the run id and an \`optionId\` taken from that list. An invented or stale id is rejected. The run resumes in the background, so the answer and the next few \`run-status\` polls (often a minute or more) can still say \`checkpoint_pending\`. \`resumePending: true\` (on the answer, or on the \`checkpoint\` block of \`run-status\`) means your selection is recorded and the run is resuming: keep polling, and do not resolve again. A successful answer with \`resumePending: false\` usually means another accept of the same decision is still being recorded, so keep polling; if \`run-status\` keeps listing the options without \`resumePending: true\`, resolving again with the same \`optionId\` is safe.
|
|
28
28
|
4. **Read the result.** When the run is complete, \`run-output\` returns the structured result as generated. Its \`stageEdits\` list marks each stage whose document the user has changed since (\`hasUserEdits: true\`). When such an entry also carries a \`documentId\`, read that document with \`document-get\` rather than relying on the structured result. An entry with \`hasUserEdits: true\` and no \`documentId\` means the stage was edited but its document is not available through \`document-get\`; use \`output\` for that stage. Use \`document-list\` and \`document-get\` to read the documents the run produced, and \`prototype-list\` / \`prototype-pack\` for any prototype.
|
|
29
29
|
|
|
30
|
-
Other run controls: \`run-cancel\` stops a run that is still in progress
|
|
30
|
+
Other run controls: \`run-cancel\` stops a run that is still in progress. It answers \`cancelled: true\` for every accepted call, a finished run included, so check \`alreadyTerminal\`: \`true\` means the run had already stopped and this call changed nothing. Its \`status\` is the run's status after the call. \`run-replay\` re-runs a run, optionally from a stage.
|
|
31
31
|
|
|
32
32
|
## Reading and writing workspace context
|
|
33
33
|
|
|
@@ -53,9 +53,9 @@ Every failure is a structured object: \`{ code, message, requestId, retryHint }\
|
|
|
53
53
|
| --- | --- | --- |
|
|
54
54
|
| \`unauthorized\` | Key missing, revoked, or wrong | Ask the person to check or rotate the key |
|
|
55
55
|
| \`unauthorized_scope\` | Key lacks the scope this tool needs | Ask the person to create a key with that permission |
|
|
56
|
-
| \`workspace_mismatch\` | Argument points outside the bound workspace
|
|
56
|
+
| \`workspace_mismatch\` | Argument points outside the bound workspace | Use ids returned by this surface; do not invent them |
|
|
57
57
|
| \`needs_sharpening\` | A run request was too thin | Add the detail the result lists, then retry |
|
|
58
|
-
| \`invalid_arguments\` | A field was the wrong shape | Fix the named fields and retry |
|
|
58
|
+
| \`invalid_arguments\` | A field was the wrong shape, or a checkpoint \`optionId\` was not one of the options offered | Fix the named fields and retry; for \`optionId\`, copy an id from the options \`run-status\` lists |
|
|
59
59
|
| \`idempotency_key_reuse\` | The idempotency key was already used for a different write | Use a new key for a new write; reuse a key only to retry the same write |
|
|
60
60
|
| \`conflict\` | State changed under you (e.g. a stale document token) | Re-read the current state, then retry |
|
|
61
61
|
| \`rate_limited\` | Daily call budget or a short spike | Wait for the reset the result reports |
|
|
@@ -19868,7 +19868,12 @@ var checkpointResolveInputSchema = external_exports.object({
|
|
|
19868
19868
|
var checkpointResolveOutputSchema = external_exports.object({
|
|
19869
19869
|
runId: external_exports.string().describe("Identifier of the resolved run."),
|
|
19870
19870
|
resolved: external_exports.literal(true).describe("The checkpoint was resolved; resume polling run-status."),
|
|
19871
|
-
resumedStatus: runStatusEnum.describe(
|
|
19871
|
+
resumedStatus: runStatusEnum.describe(
|
|
19872
|
+
"Coarse status at the time of the answer; usually checkpoint_pending while the resume is in flight."
|
|
19873
|
+
),
|
|
19874
|
+
resumePending: external_exports.boolean().optional().describe(
|
|
19875
|
+
"True when the selection is recorded and the run is resuming in the background: keep polling run-status and do not resolve again. If false, keep polling; if later polls keep listing the options without resumePending true, resolving again with the same optionId is safe."
|
|
19876
|
+
),
|
|
19872
19877
|
schemaVersion: schemaVersionSchema
|
|
19873
19878
|
});
|
|
19874
19879
|
|
|
@@ -20304,7 +20309,13 @@ var runCancelInputSchema = external_exports.object({
|
|
|
20304
20309
|
});
|
|
20305
20310
|
var runCancelOutputSchema = external_exports.object({
|
|
20306
20311
|
runId: external_exports.string().describe("Identifier of the cancelled run."),
|
|
20307
|
-
cancelled: external_exports.literal(true).describe(
|
|
20312
|
+
cancelled: external_exports.literal(true).describe(
|
|
20313
|
+
"The cancel was accepted, or the run is already stopped; check alreadyTerminal."
|
|
20314
|
+
),
|
|
20315
|
+
alreadyTerminal: external_exports.boolean().optional().describe(
|
|
20316
|
+
"True when the run had already stopped and this call changed nothing."
|
|
20317
|
+
),
|
|
20318
|
+
status: runStatusEnum.optional().describe("Coarse status of the run after this call."),
|
|
20308
20319
|
schemaVersion: schemaVersionSchema
|
|
20309
20320
|
});
|
|
20310
20321
|
var runReplayInputSchema = external_exports.object({
|
|
@@ -20421,7 +20432,10 @@ var checkpointPayloadSchema = external_exports.object({
|
|
|
20421
20432
|
options: external_exports.array(checkpointOptionSchema).describe("The strategic options to choose between."),
|
|
20422
20433
|
keyDifferences: external_exports.string().describe("How the options differ from one another."),
|
|
20423
20434
|
recommendationJustification: external_exports.string().describe("Why the recommended option is recommended."),
|
|
20424
|
-
refineUrl: external_exports.string().describe("Hosted-app URL to refine this checkpoint set.")
|
|
20435
|
+
refineUrl: external_exports.string().describe("Hosted-app URL to refine this checkpoint set."),
|
|
20436
|
+
resumePending: external_exports.boolean().optional().describe(
|
|
20437
|
+
"True when a selection is recorded and the run is resuming in the background: keep polling and do not resolve again. False after a successful checkpoint-resolve: keep polling; if later polls keep listing the options without resumePending true, resolving again with the same optionId is safe."
|
|
20438
|
+
)
|
|
20425
20439
|
});
|
|
20426
20440
|
var runStatusInputSchema = external_exports.object({
|
|
20427
20441
|
runId: external_exports.string().describe("Identifier of the run to poll."),
|
|
@@ -20509,12 +20523,14 @@ var API_KEY_SECRET_PATTERN = /^ak_[\x21-\x7E]+$/;
|
|
|
20509
20523
|
var KeyringCredentialStore = class {
|
|
20510
20524
|
async deleteBearerToken() {
|
|
20511
20525
|
const keyring = await import("@napi-rs/keyring");
|
|
20512
|
-
const removed =
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
)
|
|
20517
|
-
|
|
20526
|
+
const removed = [];
|
|
20527
|
+
if (await deleteKeyringPassword(keyring, KEYCHAIN_SERVICE)) {
|
|
20528
|
+
removed.push(keychainCredentialLocation);
|
|
20529
|
+
}
|
|
20530
|
+
if (await deleteKeyringPassword(keyring, LEGACY_KEYCHAIN_SERVICE)) {
|
|
20531
|
+
removed.push(legacyKeychainCredentialLocation);
|
|
20532
|
+
}
|
|
20533
|
+
return removed;
|
|
20518
20534
|
}
|
|
20519
20535
|
async loadBearerToken() {
|
|
20520
20536
|
const keyring = await import("@napi-rs/keyring");
|
|
@@ -20599,6 +20615,10 @@ var keychainCredentialLocation = {
|
|
|
20599
20615
|
account: KEYCHAIN_ACCOUNT,
|
|
20600
20616
|
service: KEYCHAIN_SERVICE
|
|
20601
20617
|
};
|
|
20618
|
+
var legacyKeychainCredentialLocation = {
|
|
20619
|
+
account: KEYCHAIN_ACCOUNT,
|
|
20620
|
+
service: LEGACY_KEYCHAIN_SERVICE
|
|
20621
|
+
};
|
|
20602
20622
|
|
|
20603
20623
|
// src/runtime/exit-codes.ts
|
|
20604
20624
|
var EXIT_CODES = {
|
|
@@ -20706,11 +20726,13 @@ async function createSdkMcpClient(config2) {
|
|
|
20706
20726
|
function isUnsafeTerminalCodePoint(codePoint) {
|
|
20707
20727
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 1564 || codePoint === 8206 || codePoint === 8207 || codePoint === 8232 || codePoint === 8233 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297;
|
|
20708
20728
|
}
|
|
20709
|
-
|
|
20729
|
+
var LINE_FEED_CODE_POINT = 10;
|
|
20730
|
+
function escapeTerminalText(value, maxCodePoints, options = {}) {
|
|
20710
20731
|
const codePoints = Array.from(value);
|
|
20711
20732
|
const escaped = codePoints.slice(0, maxCodePoints).map((character) => {
|
|
20712
20733
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
20713
|
-
|
|
20734
|
+
const keep = options.keepLineBreaks && codePoint === LINE_FEED_CODE_POINT;
|
|
20735
|
+
return isUnsafeTerminalCodePoint(codePoint) && !keep ? `\\u${codePoint.toString(16).padStart(4, "0")}` : character;
|
|
20714
20736
|
}).join("");
|
|
20715
20737
|
return codePoints.length > maxCodePoints ? `${escaped}\u2026` : escaped;
|
|
20716
20738
|
}
|
|
@@ -20734,6 +20756,9 @@ var API_KEY_IN_TEXT_PATTERN = /ak_[^\s"'\\]+/g;
|
|
|
20734
20756
|
var REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
20735
20757
|
var API_KEY_MARKER_PATTERN = /ak_/i;
|
|
20736
20758
|
var DEBUG_DIAGNOSTIC_MAX_CODE_POINTS = 300;
|
|
20759
|
+
var FAILURE_MESSAGE_MAX_CODE_POINTS = 1e3;
|
|
20760
|
+
var FAILURE_RETRY_HINT_MAX_CODE_POINTS = 500;
|
|
20761
|
+
var FAILURE_UPGRADE_COMMAND_MAX_CODE_POINTS = 200;
|
|
20737
20762
|
var defaultIo = {
|
|
20738
20763
|
stdin: process.stdin,
|
|
20739
20764
|
stderr: process.stderr,
|
|
@@ -20865,9 +20890,11 @@ function createCliOperationalError(input2) {
|
|
|
20865
20890
|
input2.diagnostic
|
|
20866
20891
|
);
|
|
20867
20892
|
}
|
|
20868
|
-
function renderCliError(document, options, io, diagnostic) {
|
|
20893
|
+
function renderCliError(document, options, io, diagnostic, { keepMessageLineBreaks = false } = {}) {
|
|
20869
20894
|
const exitCode2 = exitCodeForCliError(document.code);
|
|
20870
|
-
renderFailure({ diagnostic, document, exitCode: exitCode2 }, options, io
|
|
20895
|
+
renderFailure({ diagnostic, document, exitCode: exitCode2 }, options, io, {
|
|
20896
|
+
keepMessageLineBreaks
|
|
20897
|
+
});
|
|
20871
20898
|
return exitCode2;
|
|
20872
20899
|
}
|
|
20873
20900
|
function resolveRunnerDeps(overrides, io) {
|
|
@@ -21209,21 +21236,27 @@ function renderSuccess(response, spec, options, io) {
|
|
|
21209
21236
|
io.stdout.write(`${JSON.stringify(response)}
|
|
21210
21237
|
`);
|
|
21211
21238
|
}
|
|
21212
|
-
function renderFailure(failure2, options, io) {
|
|
21239
|
+
function renderFailure(failure2, options, io, { keepMessageLineBreaks = false } = {}) {
|
|
21213
21240
|
if (shouldRenderJson(options, io)) {
|
|
21214
21241
|
io.stdout.write(`${JSON.stringify(failure2.document)}
|
|
21215
21242
|
`);
|
|
21216
21243
|
} else {
|
|
21217
|
-
io.stderr.write(
|
|
21218
|
-
`)
|
|
21244
|
+
io.stderr.write(
|
|
21245
|
+
`Error: ${escapeTerminalText(failure2.document.message, FAILURE_MESSAGE_MAX_CODE_POINTS, { keepLineBreaks: keepMessageLineBreaks })}
|
|
21246
|
+
`
|
|
21247
|
+
);
|
|
21219
21248
|
const upgradeCommand = getUpgradeCommand(failure2.document.details);
|
|
21220
21249
|
if (upgradeCommand) {
|
|
21221
|
-
io.stderr.write(
|
|
21222
|
-
`)
|
|
21250
|
+
io.stderr.write(
|
|
21251
|
+
`Upgrade: ${escapeTerminalText(upgradeCommand, FAILURE_UPGRADE_COMMAND_MAX_CODE_POINTS)}
|
|
21252
|
+
`
|
|
21253
|
+
);
|
|
21223
21254
|
}
|
|
21224
21255
|
if (failure2.document.retryHint) {
|
|
21225
|
-
io.stderr.write(
|
|
21226
|
-
|
|
21256
|
+
io.stderr.write(
|
|
21257
|
+
`${escapeTerminalText(failure2.document.retryHint, FAILURE_RETRY_HINT_MAX_CODE_POINTS)}
|
|
21258
|
+
`
|
|
21259
|
+
);
|
|
21227
21260
|
}
|
|
21228
21261
|
}
|
|
21229
21262
|
printDebugRequestId(options, io, failure2.document.requestId);
|
|
@@ -21476,8 +21509,7 @@ function runCheckpointResolveCommand(options) {
|
|
|
21476
21509
|
return runCommand(
|
|
21477
21510
|
{
|
|
21478
21511
|
outputSchema: checkpointResolveOutputSchema,
|
|
21479
|
-
renderHuman:
|
|
21480
|
-
`,
|
|
21512
|
+
renderHuman: renderCheckpointResolve,
|
|
21481
21513
|
toolArguments: { runId: runId.value, optionId: optionId.value },
|
|
21482
21514
|
toolName: "checkpoint-resolve"
|
|
21483
21515
|
},
|
|
@@ -21488,6 +21520,15 @@ function runCheckpointResolveCommand(options) {
|
|
|
21488
21520
|
}
|
|
21489
21521
|
);
|
|
21490
21522
|
}
|
|
21523
|
+
function renderCheckpointResolve(response) {
|
|
21524
|
+
const poll = `plob run-status ${response.runId}`;
|
|
21525
|
+
if (response.resumePending === true) {
|
|
21526
|
+
return `Run ${response.runId}: selection recorded; the run is resuming in the background. Keep polling \`${poll}\` and don't resolve again.
|
|
21527
|
+
`;
|
|
21528
|
+
}
|
|
21529
|
+
return `Run ${response.runId}: selection accepted. Keep polling \`${poll}\`. If later polls keep listing the options without a recorded selection, resolving again with the same option id is safe.
|
|
21530
|
+
`;
|
|
21531
|
+
}
|
|
21491
21532
|
|
|
21492
21533
|
// src/commands/context-pack.ts
|
|
21493
21534
|
function registerContextPackCommand(program, options) {
|
|
@@ -22143,11 +22184,11 @@ async function runLogoutCommand(options) {
|
|
|
22143
22184
|
options.io
|
|
22144
22185
|
);
|
|
22145
22186
|
let existingSecret;
|
|
22146
|
-
let
|
|
22187
|
+
let removedEntries = [];
|
|
22147
22188
|
try {
|
|
22148
22189
|
existingSecret = await deps.credentialStore.loadBearerToken();
|
|
22149
22190
|
if (existingSecret) {
|
|
22150
|
-
|
|
22191
|
+
removedEntries = await deps.credentialStore.deleteBearerToken();
|
|
22151
22192
|
}
|
|
22152
22193
|
} catch {
|
|
22153
22194
|
return renderCliError(
|
|
@@ -22164,7 +22205,7 @@ async function runLogoutCommand(options) {
|
|
|
22164
22205
|
if (!existingSecret) {
|
|
22165
22206
|
return renderMissingCredential(options);
|
|
22166
22207
|
}
|
|
22167
|
-
if (
|
|
22208
|
+
if (removedEntries.length === 0) {
|
|
22168
22209
|
return renderCliError(
|
|
22169
22210
|
createCliErrorDocument({
|
|
22170
22211
|
code: "internal",
|
|
@@ -22178,7 +22219,8 @@ async function runLogoutCommand(options) {
|
|
|
22178
22219
|
{
|
|
22179
22220
|
data: {
|
|
22180
22221
|
keychain: keychainCredentialLocation,
|
|
22181
|
-
removed: true
|
|
22222
|
+
removed: true,
|
|
22223
|
+
removedEntries
|
|
22182
22224
|
},
|
|
22183
22225
|
schemaVersion: AGENTIC_CONTRACT_SCHEMA_VERSION
|
|
22184
22226
|
},
|
|
@@ -22211,7 +22253,11 @@ function renderLogoutSuccess(document, options, io) {
|
|
|
22211
22253
|
`);
|
|
22212
22254
|
return;
|
|
22213
22255
|
}
|
|
22214
|
-
|
|
22256
|
+
const services = document.data.removedEntries.map((entry) => entry.service).join(", ");
|
|
22257
|
+
io.stdout.write(
|
|
22258
|
+
`ProductLobster API key removed from the OS keychain: ${services}.
|
|
22259
|
+
`
|
|
22260
|
+
);
|
|
22215
22261
|
}
|
|
22216
22262
|
|
|
22217
22263
|
// src/commands/observe.ts
|
|
@@ -22604,6 +22650,19 @@ function resolveSafeTarget(root, entryName) {
|
|
|
22604
22650
|
}
|
|
22605
22651
|
|
|
22606
22652
|
// src/commands/prototype-pack.ts
|
|
22653
|
+
var SIGNED_URL_MAX_REDIRECT_HOPS = 5;
|
|
22654
|
+
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([
|
|
22655
|
+
301,
|
|
22656
|
+
302,
|
|
22657
|
+
303,
|
|
22658
|
+
307,
|
|
22659
|
+
308
|
|
22660
|
+
]);
|
|
22661
|
+
var PACK_NETWORK_FAILURE = {
|
|
22662
|
+
code: "network",
|
|
22663
|
+
message: "Unable to fetch the prototype ZIP. Check the network connection and retry.",
|
|
22664
|
+
retryHint: "Retry after the connection is available."
|
|
22665
|
+
};
|
|
22607
22666
|
function registerPrototypePackCommand(program, options) {
|
|
22608
22667
|
const command = program.command("prototype-pack").description("Fetch a prototype ZIP reference.").requiredOption("--prototype-id <id>", "Prototype id.").option("--out <dir>", "New output directory (parent must already exist).");
|
|
22609
22668
|
command.action(async (commandOptions) => {
|
|
@@ -22683,22 +22742,7 @@ async function fetchAndExtractPack(input2) {
|
|
|
22683
22742
|
message: "The prototype ZIP URL must use https. Retry once; if it continues, report the failure."
|
|
22684
22743
|
});
|
|
22685
22744
|
}
|
|
22686
|
-
|
|
22687
|
-
try {
|
|
22688
|
-
response = await fetch(input2.pack.url, {
|
|
22689
|
-
headers,
|
|
22690
|
-
// With the bearer attached, never replay it to wherever a redirect
|
|
22691
|
-
// points; the non-2xx check below reports the redirect status.
|
|
22692
|
-
...headers.authorization ? { redirect: "manual" } : {}
|
|
22693
|
-
});
|
|
22694
|
-
} catch (error62) {
|
|
22695
|
-
throw createCliOperationalError({
|
|
22696
|
-
code: "network",
|
|
22697
|
-
diagnostic: describeThrownError(error62),
|
|
22698
|
-
message: "Unable to fetch the prototype ZIP. Check the network connection and retry.",
|
|
22699
|
-
retryHint: "Retry after the connection is available."
|
|
22700
|
-
});
|
|
22701
|
-
}
|
|
22745
|
+
const response = input2.pack.delivery === "authenticated_stream" ? await fetchPack(input2.pack.url, headers) : await fetchSignedPack(input2.pack.url);
|
|
22702
22746
|
if (!response.ok) {
|
|
22703
22747
|
const failure2 = await normalizeHttpResponseFailure(
|
|
22704
22748
|
response,
|
|
@@ -22710,7 +22754,15 @@ async function fetchAndExtractPack(input2) {
|
|
|
22710
22754
|
failure2.diagnostic
|
|
22711
22755
|
);
|
|
22712
22756
|
}
|
|
22713
|
-
|
|
22757
|
+
let archive;
|
|
22758
|
+
try {
|
|
22759
|
+
archive = new Uint8Array(await response.arrayBuffer());
|
|
22760
|
+
} catch (error62) {
|
|
22761
|
+
throw createCliOperationalError({
|
|
22762
|
+
...PACK_NETWORK_FAILURE,
|
|
22763
|
+
diagnostic: `body unreadable: ${describeThrownError(error62)}`
|
|
22764
|
+
});
|
|
22765
|
+
}
|
|
22714
22766
|
try {
|
|
22715
22767
|
return await extractZipToDirectory(archive, input2.outDir);
|
|
22716
22768
|
} catch (error62) {
|
|
@@ -22726,6 +22778,53 @@ async function fetchAndExtractPack(input2) {
|
|
|
22726
22778
|
});
|
|
22727
22779
|
}
|
|
22728
22780
|
}
|
|
22781
|
+
async function fetchPack(url2, headers) {
|
|
22782
|
+
try {
|
|
22783
|
+
return await fetch(url2, { headers, redirect: "manual" });
|
|
22784
|
+
} catch (error62) {
|
|
22785
|
+
throw createCliOperationalError({
|
|
22786
|
+
...PACK_NETWORK_FAILURE,
|
|
22787
|
+
diagnostic: describeThrownError(error62)
|
|
22788
|
+
});
|
|
22789
|
+
}
|
|
22790
|
+
}
|
|
22791
|
+
async function fetchSignedPack(initialUrl) {
|
|
22792
|
+
let url2 = initialUrl;
|
|
22793
|
+
for (let hop = 0; ; hop += 1) {
|
|
22794
|
+
const response = await fetchPack(url2, {});
|
|
22795
|
+
if (!REDIRECT_STATUSES.has(response.status)) {
|
|
22796
|
+
return response;
|
|
22797
|
+
}
|
|
22798
|
+
response.body?.cancel().catch(() => void 0);
|
|
22799
|
+
if (hop === SIGNED_URL_MAX_REDIRECT_HOPS) {
|
|
22800
|
+
throw signedRedirectFailure("redirected too many times");
|
|
22801
|
+
}
|
|
22802
|
+
const location = response.headers.get("location");
|
|
22803
|
+
if (!location) {
|
|
22804
|
+
throw signedRedirectFailure("redirected without a target URL");
|
|
22805
|
+
}
|
|
22806
|
+
const next = resolveRedirectUrl(location, url2);
|
|
22807
|
+
if (!(next && isHttpsOrLoopbackUrl(next))) {
|
|
22808
|
+
throw signedRedirectFailure(
|
|
22809
|
+
"redirected to a URL that does not use https"
|
|
22810
|
+
);
|
|
22811
|
+
}
|
|
22812
|
+
url2 = next;
|
|
22813
|
+
}
|
|
22814
|
+
}
|
|
22815
|
+
function resolveRedirectUrl(location, currentUrl) {
|
|
22816
|
+
try {
|
|
22817
|
+
return new URL(location, currentUrl).href;
|
|
22818
|
+
} catch {
|
|
22819
|
+
return;
|
|
22820
|
+
}
|
|
22821
|
+
}
|
|
22822
|
+
function signedRedirectFailure(reason) {
|
|
22823
|
+
return createCliOperationalError({
|
|
22824
|
+
code: "internal",
|
|
22825
|
+
message: `The prototype ZIP download ${reason}. Retry once; if it continues, report the failure.`
|
|
22826
|
+
});
|
|
22827
|
+
}
|
|
22729
22828
|
function bundleRouteFailure(status) {
|
|
22730
22829
|
if (status === 401) {
|
|
22731
22830
|
return {
|
|
@@ -22868,8 +22967,7 @@ function runRunCancelCommand(options) {
|
|
|
22868
22967
|
return runCommand(
|
|
22869
22968
|
{
|
|
22870
22969
|
outputSchema: runCancelOutputSchema,
|
|
22871
|
-
renderHuman:
|
|
22872
|
-
`,
|
|
22970
|
+
renderHuman: renderRunCancel,
|
|
22873
22971
|
toolArguments: { runId: runId.value },
|
|
22874
22972
|
toolName: "run-cancel"
|
|
22875
22973
|
},
|
|
@@ -22880,6 +22978,15 @@ function runRunCancelCommand(options) {
|
|
|
22880
22978
|
}
|
|
22881
22979
|
);
|
|
22882
22980
|
}
|
|
22981
|
+
function renderRunCancel(response) {
|
|
22982
|
+
if (response.alreadyTerminal === true) {
|
|
22983
|
+
const status = response.status ? ` (status: ${response.status})` : "";
|
|
22984
|
+
return `Run ${response.runId} had already stopped${status}; this call changed nothing.
|
|
22985
|
+
`;
|
|
22986
|
+
}
|
|
22987
|
+
return `Run ${response.runId} cancel accepted.
|
|
22988
|
+
`;
|
|
22989
|
+
}
|
|
22883
22990
|
|
|
22884
22991
|
// src/commands/run-output.ts
|
|
22885
22992
|
function registerRunOutputCommand(program, options) {
|
|
@@ -23069,6 +23176,9 @@ function runRunStatusCommand(options) {
|
|
|
23069
23176
|
}
|
|
23070
23177
|
);
|
|
23071
23178
|
}
|
|
23179
|
+
var CHECKPOINT_QUESTION_DISPLAY_MAX = 500;
|
|
23180
|
+
var CHECKPOINT_OPTION_ID_DISPLAY_MAX = 200;
|
|
23181
|
+
var CHECKPOINT_OPTION_TITLE_DISPLAY_MAX = 200;
|
|
23072
23182
|
function renderRunStatus(response) {
|
|
23073
23183
|
const lines = [`Run ${response.runId}: ${response.status}`];
|
|
23074
23184
|
if (response.stages.length > 0) {
|
|
@@ -23081,11 +23191,7 @@ function renderRunStatus(response) {
|
|
|
23081
23191
|
lines.push(`Transitions: ${response.transitions.length}`);
|
|
23082
23192
|
}
|
|
23083
23193
|
if (response.checkpoint) {
|
|
23084
|
-
lines.push(
|
|
23085
|
-
for (const option of response.checkpoint.options) {
|
|
23086
|
-
const marker = option.recommended ? " recommended" : "";
|
|
23087
|
-
lines.push(`- ${option.id}: ${option.title}${marker}`);
|
|
23088
|
-
}
|
|
23194
|
+
lines.push(...renderCheckpoint(response.checkpoint));
|
|
23089
23195
|
}
|
|
23090
23196
|
if (response.nextCursor) {
|
|
23091
23197
|
lines.push(`Next cursor: ${response.nextCursor}`);
|
|
@@ -23093,6 +23199,26 @@ function renderRunStatus(response) {
|
|
|
23093
23199
|
return `${lines.join("\n")}
|
|
23094
23200
|
`;
|
|
23095
23201
|
}
|
|
23202
|
+
function renderCheckpoint(checkpoint) {
|
|
23203
|
+
const question = escapeTerminalText(
|
|
23204
|
+
checkpoint.question,
|
|
23205
|
+
CHECKPOINT_QUESTION_DISPLAY_MAX
|
|
23206
|
+
);
|
|
23207
|
+
if (checkpoint.resumePending === true) {
|
|
23208
|
+
return [`Decision recorded; the run is resuming. Checkpoint: ${question}`];
|
|
23209
|
+
}
|
|
23210
|
+
const lines = [`Pending decision: ${question}`];
|
|
23211
|
+
for (const option of checkpoint.options) {
|
|
23212
|
+
const id = escapeTerminalText(option.id, CHECKPOINT_OPTION_ID_DISPLAY_MAX);
|
|
23213
|
+
const title = escapeTerminalText(
|
|
23214
|
+
option.title,
|
|
23215
|
+
CHECKPOINT_OPTION_TITLE_DISPLAY_MAX
|
|
23216
|
+
);
|
|
23217
|
+
const marker = option.recommended ? " recommended" : "";
|
|
23218
|
+
lines.push(`- ${id}: ${title}${marker}`);
|
|
23219
|
+
}
|
|
23220
|
+
return lines;
|
|
23221
|
+
}
|
|
23096
23222
|
|
|
23097
23223
|
// src/commands/upload.ts
|
|
23098
23224
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
@@ -23244,11 +23370,7 @@ async function runUploadCommand(options) {
|
|
|
23244
23370
|
});
|
|
23245
23371
|
} catch (error62) {
|
|
23246
23372
|
return renderCliError(
|
|
23247
|
-
|
|
23248
|
-
code: "network",
|
|
23249
|
-
message: "Unable to reach ProductLobster. Check the network connection and retry.",
|
|
23250
|
-
retryHint: "Retry after the connection is available."
|
|
23251
|
-
}),
|
|
23373
|
+
createNetworkErrorDocument(),
|
|
23252
23374
|
options.globalOptions,
|
|
23253
23375
|
options.io,
|
|
23254
23376
|
describeThrownError(error62)
|
|
@@ -23257,7 +23379,7 @@ async function runUploadCommand(options) {
|
|
|
23257
23379
|
if (!response.ok) {
|
|
23258
23380
|
const failure2 = await normalizeHttpResponseFailure(
|
|
23259
23381
|
response,
|
|
23260
|
-
|
|
23382
|
+
createUploadRouteFailure(prepared.extension)
|
|
23261
23383
|
);
|
|
23262
23384
|
return renderCliError(
|
|
23263
23385
|
failure2.document,
|
|
@@ -23266,12 +23388,34 @@ async function runUploadCommand(options) {
|
|
|
23266
23388
|
failure2.diagnostic
|
|
23267
23389
|
);
|
|
23268
23390
|
}
|
|
23391
|
+
let bodyText;
|
|
23392
|
+
try {
|
|
23393
|
+
bodyText = await response.text();
|
|
23394
|
+
} catch (error62) {
|
|
23395
|
+
return renderCliError(
|
|
23396
|
+
createCliErrorDocument({
|
|
23397
|
+
code: "network",
|
|
23398
|
+
message: "The connection to ProductLobster closed before the upload was confirmed. The upload may already have been stored.",
|
|
23399
|
+
retryHint: "Check the workspace's documents in the ProductLobster app before uploading again."
|
|
23400
|
+
}),
|
|
23401
|
+
options.globalOptions,
|
|
23402
|
+
options.io,
|
|
23403
|
+
`body unreadable: ${describeThrownError(error62)}`
|
|
23404
|
+
);
|
|
23405
|
+
}
|
|
23269
23406
|
const uploadResponse = normalizeUploadSuccessPayload(
|
|
23270
|
-
parseResponsePayload(
|
|
23407
|
+
parseResponsePayload(bodyText)
|
|
23271
23408
|
);
|
|
23272
23409
|
renderUploadSuccess(uploadResponse, options.globalOptions, options.io);
|
|
23273
23410
|
return EXIT_CODES.success;
|
|
23274
23411
|
}
|
|
23412
|
+
function createNetworkErrorDocument() {
|
|
23413
|
+
return createCliErrorDocument({
|
|
23414
|
+
code: "network",
|
|
23415
|
+
message: "Unable to reach ProductLobster. Check the network connection and retry.",
|
|
23416
|
+
retryHint: "Retry after the connection is available."
|
|
23417
|
+
});
|
|
23418
|
+
}
|
|
23275
23419
|
function resolveDocumentUploadUrl(mcpEndpoint, workspaceId) {
|
|
23276
23420
|
const endpoint = new URL(mcpEndpoint);
|
|
23277
23421
|
return new URL(
|
|
@@ -23301,9 +23445,8 @@ async function prepareUploadFile(filePath) {
|
|
|
23301
23445
|
message: `File size ${fileStat.size} exceeds maximum of ${MAX_UPLOAD_FILE_SIZE_BYTES} bytes.`
|
|
23302
23446
|
};
|
|
23303
23447
|
}
|
|
23304
|
-
const
|
|
23305
|
-
|
|
23306
|
-
);
|
|
23448
|
+
const extension = path3.extname(normalizedPath).toLowerCase();
|
|
23449
|
+
const mimeType = MIME_TYPES_BY_EXTENSION.get(extension);
|
|
23307
23450
|
if (!mimeType) {
|
|
23308
23451
|
return {
|
|
23309
23452
|
message: "Unsupported file extension. Supported extensions: .pdf, .docx, .txt, .md, .markdown, .png, .jpg, .jpeg, .webp."
|
|
@@ -23311,6 +23454,7 @@ async function prepareUploadFile(filePath) {
|
|
|
23311
23454
|
}
|
|
23312
23455
|
const bytes = await readFile2(normalizedPath);
|
|
23313
23456
|
return {
|
|
23457
|
+
extension,
|
|
23314
23458
|
file: new File([bytes], path3.basename(normalizedPath), { type: mimeType })
|
|
23315
23459
|
};
|
|
23316
23460
|
}
|
|
@@ -23324,18 +23468,25 @@ function parseResponsePayload(text) {
|
|
|
23324
23468
|
return text;
|
|
23325
23469
|
}
|
|
23326
23470
|
}
|
|
23327
|
-
function
|
|
23328
|
-
|
|
23471
|
+
function createUploadRouteFailure(extension) {
|
|
23472
|
+
return (status, jsonBody) => {
|
|
23473
|
+
if (status >= 300 && status < 400) {
|
|
23474
|
+
return {
|
|
23475
|
+
code: "internal",
|
|
23476
|
+
message: `ProductLobster redirected the upload (HTTP ${status}) instead of accepting it. The API key was not sent on. Retry once; if it continues, report the failure.`
|
|
23477
|
+
};
|
|
23478
|
+
}
|
|
23479
|
+
if (status === 415) {
|
|
23480
|
+
return {
|
|
23481
|
+
code: "invalid_arguments",
|
|
23482
|
+
message: `ProductLobster did not accept the ${extension} file type. Upload the document in another supported format and retry.`
|
|
23483
|
+
};
|
|
23484
|
+
}
|
|
23485
|
+
const byStatus = FAILURE_BY_STATUS.get(status);
|
|
23329
23486
|
return {
|
|
23330
|
-
code:
|
|
23331
|
-
message:
|
|
23487
|
+
code: byStatus?.code,
|
|
23488
|
+
message: extractJsonErrorMessage(jsonBody) ?? byStatus?.message ?? "The upload failed. Retry once; if it continues, report the failure."
|
|
23332
23489
|
};
|
|
23333
|
-
}
|
|
23334
|
-
const routeMessage = extractJsonErrorMessage(jsonBody);
|
|
23335
|
-
const byStatus = FAILURE_BY_STATUS.get(status);
|
|
23336
|
-
return {
|
|
23337
|
-
code: byStatus?.code,
|
|
23338
|
-
message: routeMessage ?? byStatus?.message ?? "The upload failed. Retry once; if it continues, report the failure."
|
|
23339
23490
|
};
|
|
23340
23491
|
}
|
|
23341
23492
|
function extractJsonErrorMessage(jsonBody) {
|
|
@@ -23664,7 +23815,9 @@ function renderParseError(error62, argv, io) {
|
|
|
23664
23815
|
message
|
|
23665
23816
|
}),
|
|
23666
23817
|
options,
|
|
23667
|
-
io
|
|
23818
|
+
io,
|
|
23819
|
+
void 0,
|
|
23820
|
+
{ keepMessageLineBreaks: true }
|
|
23668
23821
|
);
|
|
23669
23822
|
}
|
|
23670
23823
|
function stripCommanderPrefix(message) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
|
|
3
|
+
type KeychainCredentialLocation = typeof keychainCredentialLocation | typeof legacyKeychainCredentialLocation;
|
|
3
4
|
interface CredentialStore {
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Deletes the current and the legacy entry. Resolves to the entries that
|
|
7
|
+
* existed and were removed, current first; empty when neither was removed.
|
|
8
|
+
*/
|
|
9
|
+
deleteBearerToken(): Promise<KeychainCredentialLocation[]>;
|
|
5
10
|
loadBearerToken(): Promise<string | null>;
|
|
6
11
|
saveBearerToken(secret: string): Promise<void>;
|
|
7
12
|
}
|
|
13
|
+
declare const keychainCredentialLocation: {
|
|
14
|
+
readonly account: "default";
|
|
15
|
+
readonly service: "productlobster.plob-cli";
|
|
16
|
+
};
|
|
17
|
+
declare const legacyKeychainCredentialLocation: {
|
|
18
|
+
readonly account: "default";
|
|
19
|
+
readonly service: "productlobster.pl-cli";
|
|
20
|
+
};
|
|
8
21
|
|
|
9
22
|
declare const EXIT_CODES: {
|
|
10
23
|
readonly success: 0;
|
|
@@ -108,4 +121,4 @@ declare function createProgram(config?: CliProgramConfig): {
|
|
|
108
121
|
};
|
|
109
122
|
declare function runCli(argv?: readonly string[], config?: CliProgramConfig): Promise<number>;
|
|
110
123
|
|
|
111
|
-
export { type CliExitCode, type CliIO, type CliProgramConfig, type CommandRunnerDeps, EXIT_CODES, type McpClientConfig, type McpToolClient, type RunnerGlobalOptions, createProgram, createSdkMcpClient, runCli, runCommand };
|
|
124
|
+
export { type CliExitCode, type CliIO, type CliProgramConfig, type CommandRunnerDeps, type CredentialStore, EXIT_CODES, type KeychainCredentialLocation, type McpClientConfig, type McpToolClient, type RunnerGlobalOptions, createProgram, createSdkMcpClient, runCli, runCommand };
|
package/dist/index.js
CHANGED
|
@@ -23,10 +23,10 @@ A full analysis is asynchronous and durable. The loop is the same for every work
|
|
|
23
23
|
|
|
24
24
|
1. **Start.** Call \`run-start\` with a \`workflow\` of \`launch\`, \`audit\`, \`focused-analysis\`, \`prototype\`, or \`refinement\`, plus its inputs. It returns a run id. For \`prototype\`, the returned id is a prototype id, not a run id: track it with \`prototype-list\` and fetch it with \`prototype-pack\`, not \`run-status\`. If your request is too thin, it returns a \`needs_sharpening\` result that lists what to add \u2014 add detail and call \`run-start\` again.
|
|
25
25
|
2. **Poll.** Call \`run-status\` with the run id. It reports the current status and returns a \`since\` cursor; pass that cursor on the next poll to get only what changed. Runs take minutes \u2014 poll, do not block. Stop polling when the run's \`status\` is \`completed\`, \`failed\`, or \`cancelled\`. Do not wait for every stage to leave \`pending\`: a run that fails or is cancelled part-way leaves its unstarted stages \`pending\` permanently, and on a finished run \`pending\` means the stage never ran.
|
|
26
|
-
3. **Resolve checkpoints.** When \`run-status\` reports that the run is waiting at a checkpoint, it lists the options. Pick one and call \`checkpoint-resolve\` with the run id and an \`optionId\` taken from that list. An invented or stale id is rejected.
|
|
26
|
+
3. **Resolve checkpoints.** When \`run-status\` reports that the run is waiting at a checkpoint, it lists the options. Pick one and call \`checkpoint-resolve\` with the run id and an \`optionId\` taken from that list. An invented or stale id is rejected. The run resumes in the background, so the answer and the next few \`run-status\` polls (often a minute or more) can still say \`checkpoint_pending\`. \`resumePending: true\` (on the answer, or on the \`checkpoint\` block of \`run-status\`) means your selection is recorded and the run is resuming: keep polling, and do not resolve again. A successful answer with \`resumePending: false\` usually means another accept of the same decision is still being recorded, so keep polling; if \`run-status\` keeps listing the options without \`resumePending: true\`, resolving again with the same \`optionId\` is safe.
|
|
27
27
|
4. **Read the result.** When the run is complete, \`run-output\` returns the structured result as generated. Its \`stageEdits\` list marks each stage whose document the user has changed since (\`hasUserEdits: true\`). When such an entry also carries a \`documentId\`, read that document with \`document-get\` rather than relying on the structured result. An entry with \`hasUserEdits: true\` and no \`documentId\` means the stage was edited but its document is not available through \`document-get\`; use \`output\` for that stage. Use \`document-list\` and \`document-get\` to read the documents the run produced, and \`prototype-list\` / \`prototype-pack\` for any prototype.
|
|
28
28
|
|
|
29
|
-
Other run controls: \`run-cancel\` stops a run that is still in progress
|
|
29
|
+
Other run controls: \`run-cancel\` stops a run that is still in progress. It answers \`cancelled: true\` for every accepted call, a finished run included, so check \`alreadyTerminal\`: \`true\` means the run had already stopped and this call changed nothing. Its \`status\` is the run's status after the call. \`run-replay\` re-runs a run, optionally from a stage.
|
|
30
30
|
|
|
31
31
|
## Reading and writing workspace context
|
|
32
32
|
|
|
@@ -52,9 +52,9 @@ Every failure is a structured object: \`{ code, message, requestId, retryHint }\
|
|
|
52
52
|
| --- | --- | --- |
|
|
53
53
|
| \`unauthorized\` | Key missing, revoked, or wrong | Ask the person to check or rotate the key |
|
|
54
54
|
| \`unauthorized_scope\` | Key lacks the scope this tool needs | Ask the person to create a key with that permission |
|
|
55
|
-
| \`workspace_mismatch\` | Argument points outside the bound workspace
|
|
55
|
+
| \`workspace_mismatch\` | Argument points outside the bound workspace | Use ids returned by this surface; do not invent them |
|
|
56
56
|
| \`needs_sharpening\` | A run request was too thin | Add the detail the result lists, then retry |
|
|
57
|
-
| \`invalid_arguments\` | A field was the wrong shape | Fix the named fields and retry |
|
|
57
|
+
| \`invalid_arguments\` | A field was the wrong shape, or a checkpoint \`optionId\` was not one of the options offered | Fix the named fields and retry; for \`optionId\`, copy an id from the options \`run-status\` lists |
|
|
58
58
|
| \`idempotency_key_reuse\` | The idempotency key was already used for a different write | Use a new key for a new write; reuse a key only to retry the same write |
|
|
59
59
|
| \`conflict\` | State changed under you (e.g. a stale document token) | Re-read the current state, then retry |
|
|
60
60
|
| \`rate_limited\` | Daily call budget or a short spike | Wait for the reset the result reports |
|
|
@@ -19867,7 +19867,12 @@ var checkpointResolveInputSchema = external_exports.object({
|
|
|
19867
19867
|
var checkpointResolveOutputSchema = external_exports.object({
|
|
19868
19868
|
runId: external_exports.string().describe("Identifier of the resolved run."),
|
|
19869
19869
|
resolved: external_exports.literal(true).describe("The checkpoint was resolved; resume polling run-status."),
|
|
19870
|
-
resumedStatus: runStatusEnum.describe(
|
|
19870
|
+
resumedStatus: runStatusEnum.describe(
|
|
19871
|
+
"Coarse status at the time of the answer; usually checkpoint_pending while the resume is in flight."
|
|
19872
|
+
),
|
|
19873
|
+
resumePending: external_exports.boolean().optional().describe(
|
|
19874
|
+
"True when the selection is recorded and the run is resuming in the background: keep polling run-status and do not resolve again. If false, keep polling; if later polls keep listing the options without resumePending true, resolving again with the same optionId is safe."
|
|
19875
|
+
),
|
|
19871
19876
|
schemaVersion: schemaVersionSchema
|
|
19872
19877
|
});
|
|
19873
19878
|
|
|
@@ -20303,7 +20308,13 @@ var runCancelInputSchema = external_exports.object({
|
|
|
20303
20308
|
});
|
|
20304
20309
|
var runCancelOutputSchema = external_exports.object({
|
|
20305
20310
|
runId: external_exports.string().describe("Identifier of the cancelled run."),
|
|
20306
|
-
cancelled: external_exports.literal(true).describe(
|
|
20311
|
+
cancelled: external_exports.literal(true).describe(
|
|
20312
|
+
"The cancel was accepted, or the run is already stopped; check alreadyTerminal."
|
|
20313
|
+
),
|
|
20314
|
+
alreadyTerminal: external_exports.boolean().optional().describe(
|
|
20315
|
+
"True when the run had already stopped and this call changed nothing."
|
|
20316
|
+
),
|
|
20317
|
+
status: runStatusEnum.optional().describe("Coarse status of the run after this call."),
|
|
20307
20318
|
schemaVersion: schemaVersionSchema
|
|
20308
20319
|
});
|
|
20309
20320
|
var runReplayInputSchema = external_exports.object({
|
|
@@ -20420,7 +20431,10 @@ var checkpointPayloadSchema = external_exports.object({
|
|
|
20420
20431
|
options: external_exports.array(checkpointOptionSchema).describe("The strategic options to choose between."),
|
|
20421
20432
|
keyDifferences: external_exports.string().describe("How the options differ from one another."),
|
|
20422
20433
|
recommendationJustification: external_exports.string().describe("Why the recommended option is recommended."),
|
|
20423
|
-
refineUrl: external_exports.string().describe("Hosted-app URL to refine this checkpoint set.")
|
|
20434
|
+
refineUrl: external_exports.string().describe("Hosted-app URL to refine this checkpoint set."),
|
|
20435
|
+
resumePending: external_exports.boolean().optional().describe(
|
|
20436
|
+
"True when a selection is recorded and the run is resuming in the background: keep polling and do not resolve again. False after a successful checkpoint-resolve: keep polling; if later polls keep listing the options without resumePending true, resolving again with the same optionId is safe."
|
|
20437
|
+
)
|
|
20424
20438
|
});
|
|
20425
20439
|
var runStatusInputSchema = external_exports.object({
|
|
20426
20440
|
runId: external_exports.string().describe("Identifier of the run to poll."),
|
|
@@ -20508,12 +20522,14 @@ var API_KEY_SECRET_PATTERN = /^ak_[\x21-\x7E]+$/;
|
|
|
20508
20522
|
var KeyringCredentialStore = class {
|
|
20509
20523
|
async deleteBearerToken() {
|
|
20510
20524
|
const keyring = await import("@napi-rs/keyring");
|
|
20511
|
-
const removed =
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
)
|
|
20516
|
-
|
|
20525
|
+
const removed = [];
|
|
20526
|
+
if (await deleteKeyringPassword(keyring, KEYCHAIN_SERVICE)) {
|
|
20527
|
+
removed.push(keychainCredentialLocation);
|
|
20528
|
+
}
|
|
20529
|
+
if (await deleteKeyringPassword(keyring, LEGACY_KEYCHAIN_SERVICE)) {
|
|
20530
|
+
removed.push(legacyKeychainCredentialLocation);
|
|
20531
|
+
}
|
|
20532
|
+
return removed;
|
|
20517
20533
|
}
|
|
20518
20534
|
async loadBearerToken() {
|
|
20519
20535
|
const keyring = await import("@napi-rs/keyring");
|
|
@@ -20598,6 +20614,10 @@ var keychainCredentialLocation = {
|
|
|
20598
20614
|
account: KEYCHAIN_ACCOUNT,
|
|
20599
20615
|
service: KEYCHAIN_SERVICE
|
|
20600
20616
|
};
|
|
20617
|
+
var legacyKeychainCredentialLocation = {
|
|
20618
|
+
account: KEYCHAIN_ACCOUNT,
|
|
20619
|
+
service: LEGACY_KEYCHAIN_SERVICE
|
|
20620
|
+
};
|
|
20601
20621
|
|
|
20602
20622
|
// src/runtime/exit-codes.ts
|
|
20603
20623
|
var EXIT_CODES = {
|
|
@@ -20705,11 +20725,13 @@ async function createSdkMcpClient(config2) {
|
|
|
20705
20725
|
function isUnsafeTerminalCodePoint(codePoint) {
|
|
20706
20726
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159 || codePoint === 1564 || codePoint === 8206 || codePoint === 8207 || codePoint === 8232 || codePoint === 8233 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297;
|
|
20707
20727
|
}
|
|
20708
|
-
|
|
20728
|
+
var LINE_FEED_CODE_POINT = 10;
|
|
20729
|
+
function escapeTerminalText(value, maxCodePoints, options = {}) {
|
|
20709
20730
|
const codePoints = Array.from(value);
|
|
20710
20731
|
const escaped = codePoints.slice(0, maxCodePoints).map((character) => {
|
|
20711
20732
|
const codePoint = character.codePointAt(0) ?? 0;
|
|
20712
|
-
|
|
20733
|
+
const keep = options.keepLineBreaks && codePoint === LINE_FEED_CODE_POINT;
|
|
20734
|
+
return isUnsafeTerminalCodePoint(codePoint) && !keep ? `\\u${codePoint.toString(16).padStart(4, "0")}` : character;
|
|
20713
20735
|
}).join("");
|
|
20714
20736
|
return codePoints.length > maxCodePoints ? `${escaped}\u2026` : escaped;
|
|
20715
20737
|
}
|
|
@@ -20733,6 +20755,9 @@ var API_KEY_IN_TEXT_PATTERN = /ak_[^\s"'\\]+/g;
|
|
|
20733
20755
|
var REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
20734
20756
|
var API_KEY_MARKER_PATTERN = /ak_/i;
|
|
20735
20757
|
var DEBUG_DIAGNOSTIC_MAX_CODE_POINTS = 300;
|
|
20758
|
+
var FAILURE_MESSAGE_MAX_CODE_POINTS = 1e3;
|
|
20759
|
+
var FAILURE_RETRY_HINT_MAX_CODE_POINTS = 500;
|
|
20760
|
+
var FAILURE_UPGRADE_COMMAND_MAX_CODE_POINTS = 200;
|
|
20736
20761
|
var defaultIo = {
|
|
20737
20762
|
stdin: process.stdin,
|
|
20738
20763
|
stderr: process.stderr,
|
|
@@ -20864,9 +20889,11 @@ function createCliOperationalError(input2) {
|
|
|
20864
20889
|
input2.diagnostic
|
|
20865
20890
|
);
|
|
20866
20891
|
}
|
|
20867
|
-
function renderCliError(document, options, io, diagnostic) {
|
|
20892
|
+
function renderCliError(document, options, io, diagnostic, { keepMessageLineBreaks = false } = {}) {
|
|
20868
20893
|
const exitCode = exitCodeForCliError(document.code);
|
|
20869
|
-
renderFailure({ diagnostic, document, exitCode }, options, io
|
|
20894
|
+
renderFailure({ diagnostic, document, exitCode }, options, io, {
|
|
20895
|
+
keepMessageLineBreaks
|
|
20896
|
+
});
|
|
20870
20897
|
return exitCode;
|
|
20871
20898
|
}
|
|
20872
20899
|
function resolveRunnerDeps(overrides, io) {
|
|
@@ -21208,21 +21235,27 @@ function renderSuccess(response, spec, options, io) {
|
|
|
21208
21235
|
io.stdout.write(`${JSON.stringify(response)}
|
|
21209
21236
|
`);
|
|
21210
21237
|
}
|
|
21211
|
-
function renderFailure(failure2, options, io) {
|
|
21238
|
+
function renderFailure(failure2, options, io, { keepMessageLineBreaks = false } = {}) {
|
|
21212
21239
|
if (shouldRenderJson(options, io)) {
|
|
21213
21240
|
io.stdout.write(`${JSON.stringify(failure2.document)}
|
|
21214
21241
|
`);
|
|
21215
21242
|
} else {
|
|
21216
|
-
io.stderr.write(
|
|
21217
|
-
`)
|
|
21243
|
+
io.stderr.write(
|
|
21244
|
+
`Error: ${escapeTerminalText(failure2.document.message, FAILURE_MESSAGE_MAX_CODE_POINTS, { keepLineBreaks: keepMessageLineBreaks })}
|
|
21245
|
+
`
|
|
21246
|
+
);
|
|
21218
21247
|
const upgradeCommand = getUpgradeCommand(failure2.document.details);
|
|
21219
21248
|
if (upgradeCommand) {
|
|
21220
|
-
io.stderr.write(
|
|
21221
|
-
`)
|
|
21249
|
+
io.stderr.write(
|
|
21250
|
+
`Upgrade: ${escapeTerminalText(upgradeCommand, FAILURE_UPGRADE_COMMAND_MAX_CODE_POINTS)}
|
|
21251
|
+
`
|
|
21252
|
+
);
|
|
21222
21253
|
}
|
|
21223
21254
|
if (failure2.document.retryHint) {
|
|
21224
|
-
io.stderr.write(
|
|
21225
|
-
|
|
21255
|
+
io.stderr.write(
|
|
21256
|
+
`${escapeTerminalText(failure2.document.retryHint, FAILURE_RETRY_HINT_MAX_CODE_POINTS)}
|
|
21257
|
+
`
|
|
21258
|
+
);
|
|
21226
21259
|
}
|
|
21227
21260
|
}
|
|
21228
21261
|
printDebugRequestId(options, io, failure2.document.requestId);
|
|
@@ -21475,8 +21508,7 @@ function runCheckpointResolveCommand(options) {
|
|
|
21475
21508
|
return runCommand(
|
|
21476
21509
|
{
|
|
21477
21510
|
outputSchema: checkpointResolveOutputSchema,
|
|
21478
|
-
renderHuman:
|
|
21479
|
-
`,
|
|
21511
|
+
renderHuman: renderCheckpointResolve,
|
|
21480
21512
|
toolArguments: { runId: runId.value, optionId: optionId.value },
|
|
21481
21513
|
toolName: "checkpoint-resolve"
|
|
21482
21514
|
},
|
|
@@ -21487,6 +21519,15 @@ function runCheckpointResolveCommand(options) {
|
|
|
21487
21519
|
}
|
|
21488
21520
|
);
|
|
21489
21521
|
}
|
|
21522
|
+
function renderCheckpointResolve(response) {
|
|
21523
|
+
const poll = `plob run-status ${response.runId}`;
|
|
21524
|
+
if (response.resumePending === true) {
|
|
21525
|
+
return `Run ${response.runId}: selection recorded; the run is resuming in the background. Keep polling \`${poll}\` and don't resolve again.
|
|
21526
|
+
`;
|
|
21527
|
+
}
|
|
21528
|
+
return `Run ${response.runId}: selection accepted. Keep polling \`${poll}\`. If later polls keep listing the options without a recorded selection, resolving again with the same option id is safe.
|
|
21529
|
+
`;
|
|
21530
|
+
}
|
|
21490
21531
|
|
|
21491
21532
|
// src/commands/context-pack.ts
|
|
21492
21533
|
function registerContextPackCommand(program, options) {
|
|
@@ -22142,11 +22183,11 @@ async function runLogoutCommand(options) {
|
|
|
22142
22183
|
options.io
|
|
22143
22184
|
);
|
|
22144
22185
|
let existingSecret;
|
|
22145
|
-
let
|
|
22186
|
+
let removedEntries = [];
|
|
22146
22187
|
try {
|
|
22147
22188
|
existingSecret = await deps.credentialStore.loadBearerToken();
|
|
22148
22189
|
if (existingSecret) {
|
|
22149
|
-
|
|
22190
|
+
removedEntries = await deps.credentialStore.deleteBearerToken();
|
|
22150
22191
|
}
|
|
22151
22192
|
} catch {
|
|
22152
22193
|
return renderCliError(
|
|
@@ -22163,7 +22204,7 @@ async function runLogoutCommand(options) {
|
|
|
22163
22204
|
if (!existingSecret) {
|
|
22164
22205
|
return renderMissingCredential(options);
|
|
22165
22206
|
}
|
|
22166
|
-
if (
|
|
22207
|
+
if (removedEntries.length === 0) {
|
|
22167
22208
|
return renderCliError(
|
|
22168
22209
|
createCliErrorDocument({
|
|
22169
22210
|
code: "internal",
|
|
@@ -22177,7 +22218,8 @@ async function runLogoutCommand(options) {
|
|
|
22177
22218
|
{
|
|
22178
22219
|
data: {
|
|
22179
22220
|
keychain: keychainCredentialLocation,
|
|
22180
|
-
removed: true
|
|
22221
|
+
removed: true,
|
|
22222
|
+
removedEntries
|
|
22181
22223
|
},
|
|
22182
22224
|
schemaVersion: AGENTIC_CONTRACT_SCHEMA_VERSION
|
|
22183
22225
|
},
|
|
@@ -22210,7 +22252,11 @@ function renderLogoutSuccess(document, options, io) {
|
|
|
22210
22252
|
`);
|
|
22211
22253
|
return;
|
|
22212
22254
|
}
|
|
22213
|
-
|
|
22255
|
+
const services = document.data.removedEntries.map((entry) => entry.service).join(", ");
|
|
22256
|
+
io.stdout.write(
|
|
22257
|
+
`ProductLobster API key removed from the OS keychain: ${services}.
|
|
22258
|
+
`
|
|
22259
|
+
);
|
|
22214
22260
|
}
|
|
22215
22261
|
|
|
22216
22262
|
// src/commands/observe.ts
|
|
@@ -22603,6 +22649,19 @@ function resolveSafeTarget(root, entryName) {
|
|
|
22603
22649
|
}
|
|
22604
22650
|
|
|
22605
22651
|
// src/commands/prototype-pack.ts
|
|
22652
|
+
var SIGNED_URL_MAX_REDIRECT_HOPS = 5;
|
|
22653
|
+
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([
|
|
22654
|
+
301,
|
|
22655
|
+
302,
|
|
22656
|
+
303,
|
|
22657
|
+
307,
|
|
22658
|
+
308
|
|
22659
|
+
]);
|
|
22660
|
+
var PACK_NETWORK_FAILURE = {
|
|
22661
|
+
code: "network",
|
|
22662
|
+
message: "Unable to fetch the prototype ZIP. Check the network connection and retry.",
|
|
22663
|
+
retryHint: "Retry after the connection is available."
|
|
22664
|
+
};
|
|
22606
22665
|
function registerPrototypePackCommand(program, options) {
|
|
22607
22666
|
const command = program.command("prototype-pack").description("Fetch a prototype ZIP reference.").requiredOption("--prototype-id <id>", "Prototype id.").option("--out <dir>", "New output directory (parent must already exist).");
|
|
22608
22667
|
command.action(async (commandOptions) => {
|
|
@@ -22682,22 +22741,7 @@ async function fetchAndExtractPack(input2) {
|
|
|
22682
22741
|
message: "The prototype ZIP URL must use https. Retry once; if it continues, report the failure."
|
|
22683
22742
|
});
|
|
22684
22743
|
}
|
|
22685
|
-
|
|
22686
|
-
try {
|
|
22687
|
-
response = await fetch(input2.pack.url, {
|
|
22688
|
-
headers,
|
|
22689
|
-
// With the bearer attached, never replay it to wherever a redirect
|
|
22690
|
-
// points; the non-2xx check below reports the redirect status.
|
|
22691
|
-
...headers.authorization ? { redirect: "manual" } : {}
|
|
22692
|
-
});
|
|
22693
|
-
} catch (error62) {
|
|
22694
|
-
throw createCliOperationalError({
|
|
22695
|
-
code: "network",
|
|
22696
|
-
diagnostic: describeThrownError(error62),
|
|
22697
|
-
message: "Unable to fetch the prototype ZIP. Check the network connection and retry.",
|
|
22698
|
-
retryHint: "Retry after the connection is available."
|
|
22699
|
-
});
|
|
22700
|
-
}
|
|
22744
|
+
const response = input2.pack.delivery === "authenticated_stream" ? await fetchPack(input2.pack.url, headers) : await fetchSignedPack(input2.pack.url);
|
|
22701
22745
|
if (!response.ok) {
|
|
22702
22746
|
const failure2 = await normalizeHttpResponseFailure(
|
|
22703
22747
|
response,
|
|
@@ -22709,7 +22753,15 @@ async function fetchAndExtractPack(input2) {
|
|
|
22709
22753
|
failure2.diagnostic
|
|
22710
22754
|
);
|
|
22711
22755
|
}
|
|
22712
|
-
|
|
22756
|
+
let archive;
|
|
22757
|
+
try {
|
|
22758
|
+
archive = new Uint8Array(await response.arrayBuffer());
|
|
22759
|
+
} catch (error62) {
|
|
22760
|
+
throw createCliOperationalError({
|
|
22761
|
+
...PACK_NETWORK_FAILURE,
|
|
22762
|
+
diagnostic: `body unreadable: ${describeThrownError(error62)}`
|
|
22763
|
+
});
|
|
22764
|
+
}
|
|
22713
22765
|
try {
|
|
22714
22766
|
return await extractZipToDirectory(archive, input2.outDir);
|
|
22715
22767
|
} catch (error62) {
|
|
@@ -22725,6 +22777,53 @@ async function fetchAndExtractPack(input2) {
|
|
|
22725
22777
|
});
|
|
22726
22778
|
}
|
|
22727
22779
|
}
|
|
22780
|
+
async function fetchPack(url2, headers) {
|
|
22781
|
+
try {
|
|
22782
|
+
return await fetch(url2, { headers, redirect: "manual" });
|
|
22783
|
+
} catch (error62) {
|
|
22784
|
+
throw createCliOperationalError({
|
|
22785
|
+
...PACK_NETWORK_FAILURE,
|
|
22786
|
+
diagnostic: describeThrownError(error62)
|
|
22787
|
+
});
|
|
22788
|
+
}
|
|
22789
|
+
}
|
|
22790
|
+
async function fetchSignedPack(initialUrl) {
|
|
22791
|
+
let url2 = initialUrl;
|
|
22792
|
+
for (let hop = 0; ; hop += 1) {
|
|
22793
|
+
const response = await fetchPack(url2, {});
|
|
22794
|
+
if (!REDIRECT_STATUSES.has(response.status)) {
|
|
22795
|
+
return response;
|
|
22796
|
+
}
|
|
22797
|
+
response.body?.cancel().catch(() => void 0);
|
|
22798
|
+
if (hop === SIGNED_URL_MAX_REDIRECT_HOPS) {
|
|
22799
|
+
throw signedRedirectFailure("redirected too many times");
|
|
22800
|
+
}
|
|
22801
|
+
const location = response.headers.get("location");
|
|
22802
|
+
if (!location) {
|
|
22803
|
+
throw signedRedirectFailure("redirected without a target URL");
|
|
22804
|
+
}
|
|
22805
|
+
const next = resolveRedirectUrl(location, url2);
|
|
22806
|
+
if (!(next && isHttpsOrLoopbackUrl(next))) {
|
|
22807
|
+
throw signedRedirectFailure(
|
|
22808
|
+
"redirected to a URL that does not use https"
|
|
22809
|
+
);
|
|
22810
|
+
}
|
|
22811
|
+
url2 = next;
|
|
22812
|
+
}
|
|
22813
|
+
}
|
|
22814
|
+
function resolveRedirectUrl(location, currentUrl) {
|
|
22815
|
+
try {
|
|
22816
|
+
return new URL(location, currentUrl).href;
|
|
22817
|
+
} catch {
|
|
22818
|
+
return;
|
|
22819
|
+
}
|
|
22820
|
+
}
|
|
22821
|
+
function signedRedirectFailure(reason) {
|
|
22822
|
+
return createCliOperationalError({
|
|
22823
|
+
code: "internal",
|
|
22824
|
+
message: `The prototype ZIP download ${reason}. Retry once; if it continues, report the failure.`
|
|
22825
|
+
});
|
|
22826
|
+
}
|
|
22728
22827
|
function bundleRouteFailure(status) {
|
|
22729
22828
|
if (status === 401) {
|
|
22730
22829
|
return {
|
|
@@ -22867,8 +22966,7 @@ function runRunCancelCommand(options) {
|
|
|
22867
22966
|
return runCommand(
|
|
22868
22967
|
{
|
|
22869
22968
|
outputSchema: runCancelOutputSchema,
|
|
22870
|
-
renderHuman:
|
|
22871
|
-
`,
|
|
22969
|
+
renderHuman: renderRunCancel,
|
|
22872
22970
|
toolArguments: { runId: runId.value },
|
|
22873
22971
|
toolName: "run-cancel"
|
|
22874
22972
|
},
|
|
@@ -22879,6 +22977,15 @@ function runRunCancelCommand(options) {
|
|
|
22879
22977
|
}
|
|
22880
22978
|
);
|
|
22881
22979
|
}
|
|
22980
|
+
function renderRunCancel(response) {
|
|
22981
|
+
if (response.alreadyTerminal === true) {
|
|
22982
|
+
const status = response.status ? ` (status: ${response.status})` : "";
|
|
22983
|
+
return `Run ${response.runId} had already stopped${status}; this call changed nothing.
|
|
22984
|
+
`;
|
|
22985
|
+
}
|
|
22986
|
+
return `Run ${response.runId} cancel accepted.
|
|
22987
|
+
`;
|
|
22988
|
+
}
|
|
22882
22989
|
|
|
22883
22990
|
// src/commands/run-output.ts
|
|
22884
22991
|
function registerRunOutputCommand(program, options) {
|
|
@@ -23068,6 +23175,9 @@ function runRunStatusCommand(options) {
|
|
|
23068
23175
|
}
|
|
23069
23176
|
);
|
|
23070
23177
|
}
|
|
23178
|
+
var CHECKPOINT_QUESTION_DISPLAY_MAX = 500;
|
|
23179
|
+
var CHECKPOINT_OPTION_ID_DISPLAY_MAX = 200;
|
|
23180
|
+
var CHECKPOINT_OPTION_TITLE_DISPLAY_MAX = 200;
|
|
23071
23181
|
function renderRunStatus(response) {
|
|
23072
23182
|
const lines = [`Run ${response.runId}: ${response.status}`];
|
|
23073
23183
|
if (response.stages.length > 0) {
|
|
@@ -23080,11 +23190,7 @@ function renderRunStatus(response) {
|
|
|
23080
23190
|
lines.push(`Transitions: ${response.transitions.length}`);
|
|
23081
23191
|
}
|
|
23082
23192
|
if (response.checkpoint) {
|
|
23083
|
-
lines.push(
|
|
23084
|
-
for (const option of response.checkpoint.options) {
|
|
23085
|
-
const marker = option.recommended ? " recommended" : "";
|
|
23086
|
-
lines.push(`- ${option.id}: ${option.title}${marker}`);
|
|
23087
|
-
}
|
|
23193
|
+
lines.push(...renderCheckpoint(response.checkpoint));
|
|
23088
23194
|
}
|
|
23089
23195
|
if (response.nextCursor) {
|
|
23090
23196
|
lines.push(`Next cursor: ${response.nextCursor}`);
|
|
@@ -23092,6 +23198,26 @@ function renderRunStatus(response) {
|
|
|
23092
23198
|
return `${lines.join("\n")}
|
|
23093
23199
|
`;
|
|
23094
23200
|
}
|
|
23201
|
+
function renderCheckpoint(checkpoint) {
|
|
23202
|
+
const question = escapeTerminalText(
|
|
23203
|
+
checkpoint.question,
|
|
23204
|
+
CHECKPOINT_QUESTION_DISPLAY_MAX
|
|
23205
|
+
);
|
|
23206
|
+
if (checkpoint.resumePending === true) {
|
|
23207
|
+
return [`Decision recorded; the run is resuming. Checkpoint: ${question}`];
|
|
23208
|
+
}
|
|
23209
|
+
const lines = [`Pending decision: ${question}`];
|
|
23210
|
+
for (const option of checkpoint.options) {
|
|
23211
|
+
const id = escapeTerminalText(option.id, CHECKPOINT_OPTION_ID_DISPLAY_MAX);
|
|
23212
|
+
const title = escapeTerminalText(
|
|
23213
|
+
option.title,
|
|
23214
|
+
CHECKPOINT_OPTION_TITLE_DISPLAY_MAX
|
|
23215
|
+
);
|
|
23216
|
+
const marker = option.recommended ? " recommended" : "";
|
|
23217
|
+
lines.push(`- ${id}: ${title}${marker}`);
|
|
23218
|
+
}
|
|
23219
|
+
return lines;
|
|
23220
|
+
}
|
|
23095
23221
|
|
|
23096
23222
|
// src/commands/upload.ts
|
|
23097
23223
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
@@ -23243,11 +23369,7 @@ async function runUploadCommand(options) {
|
|
|
23243
23369
|
});
|
|
23244
23370
|
} catch (error62) {
|
|
23245
23371
|
return renderCliError(
|
|
23246
|
-
|
|
23247
|
-
code: "network",
|
|
23248
|
-
message: "Unable to reach ProductLobster. Check the network connection and retry.",
|
|
23249
|
-
retryHint: "Retry after the connection is available."
|
|
23250
|
-
}),
|
|
23372
|
+
createNetworkErrorDocument(),
|
|
23251
23373
|
options.globalOptions,
|
|
23252
23374
|
options.io,
|
|
23253
23375
|
describeThrownError(error62)
|
|
@@ -23256,7 +23378,7 @@ async function runUploadCommand(options) {
|
|
|
23256
23378
|
if (!response.ok) {
|
|
23257
23379
|
const failure2 = await normalizeHttpResponseFailure(
|
|
23258
23380
|
response,
|
|
23259
|
-
|
|
23381
|
+
createUploadRouteFailure(prepared.extension)
|
|
23260
23382
|
);
|
|
23261
23383
|
return renderCliError(
|
|
23262
23384
|
failure2.document,
|
|
@@ -23265,12 +23387,34 @@ async function runUploadCommand(options) {
|
|
|
23265
23387
|
failure2.diagnostic
|
|
23266
23388
|
);
|
|
23267
23389
|
}
|
|
23390
|
+
let bodyText;
|
|
23391
|
+
try {
|
|
23392
|
+
bodyText = await response.text();
|
|
23393
|
+
} catch (error62) {
|
|
23394
|
+
return renderCliError(
|
|
23395
|
+
createCliErrorDocument({
|
|
23396
|
+
code: "network",
|
|
23397
|
+
message: "The connection to ProductLobster closed before the upload was confirmed. The upload may already have been stored.",
|
|
23398
|
+
retryHint: "Check the workspace's documents in the ProductLobster app before uploading again."
|
|
23399
|
+
}),
|
|
23400
|
+
options.globalOptions,
|
|
23401
|
+
options.io,
|
|
23402
|
+
`body unreadable: ${describeThrownError(error62)}`
|
|
23403
|
+
);
|
|
23404
|
+
}
|
|
23268
23405
|
const uploadResponse = normalizeUploadSuccessPayload(
|
|
23269
|
-
parseResponsePayload(
|
|
23406
|
+
parseResponsePayload(bodyText)
|
|
23270
23407
|
);
|
|
23271
23408
|
renderUploadSuccess(uploadResponse, options.globalOptions, options.io);
|
|
23272
23409
|
return EXIT_CODES.success;
|
|
23273
23410
|
}
|
|
23411
|
+
function createNetworkErrorDocument() {
|
|
23412
|
+
return createCliErrorDocument({
|
|
23413
|
+
code: "network",
|
|
23414
|
+
message: "Unable to reach ProductLobster. Check the network connection and retry.",
|
|
23415
|
+
retryHint: "Retry after the connection is available."
|
|
23416
|
+
});
|
|
23417
|
+
}
|
|
23274
23418
|
function resolveDocumentUploadUrl(mcpEndpoint, workspaceId) {
|
|
23275
23419
|
const endpoint = new URL(mcpEndpoint);
|
|
23276
23420
|
return new URL(
|
|
@@ -23300,9 +23444,8 @@ async function prepareUploadFile(filePath) {
|
|
|
23300
23444
|
message: `File size ${fileStat.size} exceeds maximum of ${MAX_UPLOAD_FILE_SIZE_BYTES} bytes.`
|
|
23301
23445
|
};
|
|
23302
23446
|
}
|
|
23303
|
-
const
|
|
23304
|
-
|
|
23305
|
-
);
|
|
23447
|
+
const extension = path3.extname(normalizedPath).toLowerCase();
|
|
23448
|
+
const mimeType = MIME_TYPES_BY_EXTENSION.get(extension);
|
|
23306
23449
|
if (!mimeType) {
|
|
23307
23450
|
return {
|
|
23308
23451
|
message: "Unsupported file extension. Supported extensions: .pdf, .docx, .txt, .md, .markdown, .png, .jpg, .jpeg, .webp."
|
|
@@ -23310,6 +23453,7 @@ async function prepareUploadFile(filePath) {
|
|
|
23310
23453
|
}
|
|
23311
23454
|
const bytes = await readFile2(normalizedPath);
|
|
23312
23455
|
return {
|
|
23456
|
+
extension,
|
|
23313
23457
|
file: new File([bytes], path3.basename(normalizedPath), { type: mimeType })
|
|
23314
23458
|
};
|
|
23315
23459
|
}
|
|
@@ -23323,18 +23467,25 @@ function parseResponsePayload(text) {
|
|
|
23323
23467
|
return text;
|
|
23324
23468
|
}
|
|
23325
23469
|
}
|
|
23326
|
-
function
|
|
23327
|
-
|
|
23470
|
+
function createUploadRouteFailure(extension) {
|
|
23471
|
+
return (status, jsonBody) => {
|
|
23472
|
+
if (status >= 300 && status < 400) {
|
|
23473
|
+
return {
|
|
23474
|
+
code: "internal",
|
|
23475
|
+
message: `ProductLobster redirected the upload (HTTP ${status}) instead of accepting it. The API key was not sent on. Retry once; if it continues, report the failure.`
|
|
23476
|
+
};
|
|
23477
|
+
}
|
|
23478
|
+
if (status === 415) {
|
|
23479
|
+
return {
|
|
23480
|
+
code: "invalid_arguments",
|
|
23481
|
+
message: `ProductLobster did not accept the ${extension} file type. Upload the document in another supported format and retry.`
|
|
23482
|
+
};
|
|
23483
|
+
}
|
|
23484
|
+
const byStatus = FAILURE_BY_STATUS.get(status);
|
|
23328
23485
|
return {
|
|
23329
|
-
code:
|
|
23330
|
-
message:
|
|
23486
|
+
code: byStatus?.code,
|
|
23487
|
+
message: extractJsonErrorMessage(jsonBody) ?? byStatus?.message ?? "The upload failed. Retry once; if it continues, report the failure."
|
|
23331
23488
|
};
|
|
23332
|
-
}
|
|
23333
|
-
const routeMessage = extractJsonErrorMessage(jsonBody);
|
|
23334
|
-
const byStatus = FAILURE_BY_STATUS.get(status);
|
|
23335
|
-
return {
|
|
23336
|
-
code: byStatus?.code,
|
|
23337
|
-
message: routeMessage ?? byStatus?.message ?? "The upload failed. Retry once; if it continues, report the failure."
|
|
23338
23489
|
};
|
|
23339
23490
|
}
|
|
23340
23491
|
function extractJsonErrorMessage(jsonBody) {
|
|
@@ -23663,7 +23814,9 @@ function renderParseError(error62, argv, io) {
|
|
|
23663
23814
|
message
|
|
23664
23815
|
}),
|
|
23665
23816
|
options,
|
|
23666
|
-
io
|
|
23817
|
+
io,
|
|
23818
|
+
void 0,
|
|
23819
|
+
{ keepMessageLineBreaks: true }
|
|
23667
23820
|
);
|
|
23668
23821
|
}
|
|
23669
23822
|
function stripCommanderPrefix(message) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productlobster/plob",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "ProductLobster command-line tool — run PM analyses, manage workspaces, read run output, and upload documents from your terminal. A thin MCP client for the ProductLobster agentic surface.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ProductLobster",
|