@skrr-ai/cli 0.1.42 → 0.1.44
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/base-command.d.ts +2 -0
- package/dist/base-command.js +1 -0
- package/dist/commands/balance/index.js +1 -1
- package/dist/commands/balance/overage.js +4 -3
- package/dist/commands/balance/plan.js +10 -6
- package/dist/commands/balance/show.d.ts +32 -0
- package/dist/commands/balance/show.js +74 -3
- package/dist/commands/balance/usage/events.js +3 -2
- package/dist/commands/balance/usage.js +2 -1
- package/dist/commands/browser/install.js +3 -2
- package/dist/commands/browser/uninstall.js +3 -2
- package/dist/commands/code/handover.d.ts +1 -0
- package/dist/commands/code/handover.js +4 -0
- package/dist/commands/code/install.js +3 -2
- package/dist/commands/code/jobs/run.d.ts +1 -0
- package/dist/commands/code/jobs/run.js +4 -0
- package/dist/commands/daemon/install.js +7 -0
- package/dist/commands/harnesses/install.d.ts +11 -0
- package/dist/commands/harnesses/install.js +33 -14
- package/dist/commands/harnesses/installers.d.ts +10 -0
- package/dist/commands/harnesses/installers.js +31 -0
- package/dist/commands/harnesses/leases/show.js +14 -0
- package/dist/commands/harnesses/list.js +3 -2
- package/dist/commands/instructions/install.d.ts +22 -0
- package/dist/commands/instructions/install.js +83 -7
- package/dist/commands/instructions/list.js +5 -0
- package/dist/commands/instructions/show.d.ts +6 -0
- package/dist/commands/instructions/show.js +34 -1
- package/dist/commands/instructions/status.js +17 -1
- package/dist/commands/payments/wallet.js +2 -2
- package/dist/commands/tasks/attachments/set-role.d.ts +24 -0
- package/dist/commands/tasks/attachments/set-role.js +60 -0
- package/dist/commands/tasks/attachments/upload.d.ts +1 -0
- package/dist/commands/tasks/attachments/upload.js +18 -52
- package/dist/commands/tasks/comments/add.d.ts +1 -0
- package/dist/commands/tasks/comments/add.js +67 -2
- package/dist/commands/tasks/complete.d.ts +1 -0
- package/dist/commands/tasks/complete.js +56 -1
- package/dist/commands/tasks/create.d.ts +6 -0
- package/dist/commands/tasks/create.js +26 -3
- package/dist/commands/tasks/labels/attach.js +4 -0
- package/dist/commands/tasks/list.d.ts +1 -0
- package/dist/commands/tasks/list.js +9 -0
- package/dist/commands/tasks/result/submit.d.ts +3 -0
- package/dist/commands/tasks/result/submit.js +76 -8
- package/dist/commands/tasks/show.d.ts +23 -0
- package/dist/commands/tasks/show.js +61 -1
- package/dist/commands/tasks/update.js +16 -2
- package/dist/commands/tasks/updates/add.d.ts +1 -0
- package/dist/commands/tasks/updates/add.js +63 -0
- package/dist/commands/views/create.js +12 -2
- package/dist/commands/views/list.js +3 -2
- package/dist/commands/views/show.js +2 -0
- package/dist/lib/agentic-stream.d.ts +10 -4
- package/dist/lib/agentic-stream.js +25 -11
- package/dist/lib/api-fetch.js +7 -2
- package/dist/lib/cli-installers.d.ts +23 -4
- package/dist/lib/cli-installers.js +56 -8
- package/dist/lib/daemon-setup.d.ts +18 -1
- package/dist/lib/daemon-setup.js +33 -1
- package/dist/lib/dedicated-machines.js +4 -2
- package/dist/lib/file-mime.js +1 -1
- package/dist/lib/first-party-harness-agent.d.ts +16 -1
- package/dist/lib/first-party-harness-agent.js +47 -17
- package/dist/lib/first-party-harness-doctor.js +49 -48
- package/dist/lib/first-party-harness-managed.d.ts +9 -4
- package/dist/lib/first-party-harness-managed.js +13 -10
- package/dist/lib/first-party-harness.d.ts +32 -21
- package/dist/lib/first-party-harness.js +49 -31
- package/dist/lib/harness-provider-input.d.ts +18 -12
- package/dist/lib/harness-provider-input.js +18 -12
- package/dist/lib/harness-tiers.d.ts +4 -3
- package/dist/lib/harness-tiers.js +4 -3
- package/dist/lib/harnesses.d.ts +6 -0
- package/dist/lib/html-text.js +25 -0
- package/dist/lib/instruction-input.d.ts +21 -0
- package/dist/lib/instruction-input.js +30 -0
- package/dist/lib/instruction-provenance.d.ts +54 -0
- package/dist/lib/instruction-provenance.js +84 -0
- package/dist/lib/session-task-endpoints.d.ts +1 -1
- package/dist/lib/session-task-endpoints.js +3 -0
- package/dist/lib/task-asset-upload.d.ts +84 -0
- package/dist/lib/task-asset-upload.js +374 -0
- package/dist/lib/task-closure.d.ts +15 -0
- package/dist/lib/task-closure.js +20 -0
- package/dist/lib/task-view-render.d.ts +14 -0
- package/dist/lib/task-view-render.js +55 -0
- package/dist/lib/tasks.d.ts +26 -0
- package/dist/lib/tasks.js +38 -1
- package/dist/lib/views/vocabulary.d.ts +1 -1
- package/dist/lib/views/vocabulary.js +3 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +183 -58
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +286 -100
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +21 -33
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +24 -50
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.d.ts +19 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessHome.js +26 -10
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.d.ts +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +1 -13
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.d.ts +21 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/legacyStatePreflight.js +75 -19
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +183 -58
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +279 -99
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +21 -33
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +24 -49
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.d.ts +19 -7
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessHome.js +27 -11
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.d.ts +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +9 -5
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.d.ts +21 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/legacyStatePreflight.js +74 -19
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4588 -4509
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +33980 -33832
- package/package.json +3 -3
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Harness-valued INPUT, normalised at the edge.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* INGRESS: every flag, setting and filter that takes a harness name accepts
|
|
6
|
+
* exactly the identity's ACCEPTED spellings (`firstPartyHarnessSpellings`) and
|
|
7
|
+
* hands the rest of the CLI ONE value, so no comparison downstream has to know
|
|
8
|
+
* whether there were several (`docs/architecture/skrr-code-identifier-rename-2026-09-13.md`
|
|
9
|
+
* §2). During a rename's alias window that is the canonical spelling plus
|
|
10
|
+
* `FIRST_PARTY_HARNESS.aliases`, so a script written against either keeps
|
|
11
|
+
* working; outside one it is the canonical spelling alone. A FORMER spelling is
|
|
12
|
+
* accepted on no input — it is passed through unchanged, so a flag offering only
|
|
13
|
+
* the accepted spellings rejects it — even though the CLI still recognises it on
|
|
14
|
+
* an engine binary already on disk (`first-party-harness.ts`).
|
|
11
15
|
*
|
|
12
16
|
* Two destinations, two spellings, and the difference is the point:
|
|
13
17
|
*
|
|
14
18
|
* - to the SERVER (HTTP bodies, query params) and to the terminal: CANONICAL,
|
|
15
19
|
* which is what the platform stores and returns;
|
|
16
|
-
* - to the local DAEMON binary: the WIRE spelling
|
|
17
|
-
*
|
|
18
|
-
* the rename cannot be taught a new spelling by the CLI that
|
|
20
|
+
* - to the local DAEMON binary: the WIRE spelling (`wireProvider`). It sits on
|
|
21
|
+
* the old spelling from a rename's flip until its wire phase, because a daemon
|
|
22
|
+
* that predates the rename cannot be taught a new spelling by the CLI that
|
|
23
|
+
* talks to it, and is canonical otherwise.
|
|
19
24
|
*
|
|
20
25
|
* Every other harness name passes through unchanged — this normalises one
|
|
21
26
|
* harness's spellings, it is not a general lower-caser, and silently rewriting a
|
|
@@ -48,9 +53,10 @@ function daemonHarnessArgument(value) {
|
|
|
48
53
|
}
|
|
49
54
|
/**
|
|
50
55
|
* The `options` list for an oclif flag that enumerates harness names: the given
|
|
51
|
-
* names with every spelling of the first-party harness in place of its
|
|
52
|
-
* one, so oclif's own validation accepts an alias instead of rejecting
|
|
53
|
-
* the command ever runs
|
|
56
|
+
* names with every ACCEPTED spelling of the first-party harness in place of its
|
|
57
|
+
* canonical one, so oclif's own validation accepts an alias instead of rejecting
|
|
58
|
+
* it before the command ever runs — and rejects a former spelling, which no
|
|
59
|
+
* ingress accepts.
|
|
54
60
|
*/
|
|
55
61
|
function harnessFlagOptions(names) {
|
|
56
62
|
return canonicalHarnessInputs(names).flatMap((name) => (0, auth_core_1.isFirstPartyHarnessProvider)(name) ? [...(0, auth_core_1.firstPartyHarnessSpellings)()] : [name]);
|
|
@@ -47,9 +47,10 @@ export declare const HARNESS_TIER_AUTHORITY = "daemon/src/harness-trust.ts";
|
|
|
47
47
|
/**
|
|
48
48
|
* The tier the shared table records for a harness, or `null` for an unknown name.
|
|
49
49
|
*
|
|
50
|
-
* Canonicalised first, so no spelling of the first-party harness can reach
|
|
51
|
-
* lookup as an unknown name — the shared table also carries every
|
|
52
|
-
* this keeps the answer right even for one written in a different
|
|
50
|
+
* Canonicalised first, so no accepted spelling of the first-party harness can reach
|
|
51
|
+
* the lookup as an unknown name — the shared table also carries every accepted
|
|
52
|
+
* spelling, and this keeps the answer right even for one written in a different
|
|
53
|
+
* case. A retired spelling is an unknown name here, as everywhere trust is decided.
|
|
53
54
|
*/
|
|
54
55
|
export declare function mirroredTier(harness: string): HarnessTrustTier | null;
|
|
55
56
|
export interface TierDescription {
|
|
@@ -52,9 +52,10 @@ exports.HARNESS_TIER_AUTHORITY = 'daemon/src/harness-trust.ts';
|
|
|
52
52
|
/**
|
|
53
53
|
* The tier the shared table records for a harness, or `null` for an unknown name.
|
|
54
54
|
*
|
|
55
|
-
* Canonicalised first, so no spelling of the first-party harness can reach
|
|
56
|
-
* lookup as an unknown name — the shared table also carries every
|
|
57
|
-
* this keeps the answer right even for one written in a different
|
|
55
|
+
* Canonicalised first, so no accepted spelling of the first-party harness can reach
|
|
56
|
+
* the lookup as an unknown name — the shared table also carries every accepted
|
|
57
|
+
* spelling, and this keeps the answer right even for one written in a different
|
|
58
|
+
* case. A retired spelling is an unknown name here, as everywhere trust is decided.
|
|
58
59
|
*/
|
|
59
60
|
function mirroredTier(harness) {
|
|
60
61
|
const key = String((0, auth_core_1.canonicalHarnessProvider)(harness));
|
package/dist/lib/harnesses.d.ts
CHANGED
|
@@ -101,6 +101,12 @@ export interface HarnessLease {
|
|
|
101
101
|
machineProduct?: string;
|
|
102
102
|
machineProductLabel?: string;
|
|
103
103
|
displayName?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Who pays: the workspace whose account the lease bills, or `null` for the
|
|
106
|
+
* owner's personal account (`MachineLease.toSafeJSON`). Absent from a server
|
|
107
|
+
* that predates it, which is not the same as personal.
|
|
108
|
+
*/
|
|
109
|
+
workspaceId?: string | null;
|
|
104
110
|
state?: string;
|
|
105
111
|
stateReason?: string;
|
|
106
112
|
lifecycleSemantics?: string;
|
package/dist/lib/html-text.js
CHANGED
|
@@ -19,10 +19,35 @@ exports.htmlToDisplayText = htmlToDisplayText;
|
|
|
19
19
|
function looksLikeHtml(value) {
|
|
20
20
|
return /<\/?[a-z][^>]*>/i.test(value);
|
|
21
21
|
}
|
|
22
|
+
/** Pull one attribute out of a tag body — `src="…"`, `src='…'`, `src=…`. */
|
|
23
|
+
function tagAttr(tag, name) {
|
|
24
|
+
const m = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, 'i').exec(tag);
|
|
25
|
+
return m ? (m[1] ?? m[2] ?? m[3] ?? '') : '';
|
|
26
|
+
}
|
|
22
27
|
function htmlToDisplayText(value) {
|
|
23
28
|
if (!looksLikeHtml(value))
|
|
24
29
|
return value;
|
|
25
30
|
return (value
|
|
31
|
+
// A <video> is the same kind of meaning as an <img>, and the tag strip
|
|
32
|
+
// would erase it the same way, leaving a terminal reader no sign the
|
|
33
|
+
// description holds a recording at all. Markdown has no video syntax, so
|
|
34
|
+
// it becomes a labelled link to the stable reference. The element's
|
|
35
|
+
// children — `<source>` and fallback text — go with it; a `<source>` src
|
|
36
|
+
// is used when the element itself carries none (OSK-10129).
|
|
37
|
+
.replace(/<video\b([^>]*)>([\s\S]*?)<\/video\s*>|<video\b([^>]*)\/?>/gi, (_m, open, inner, bare) => {
|
|
38
|
+
const tag = String(open ?? bare ?? '');
|
|
39
|
+
const src = tagAttr(tag, 'src') || tagAttr(/<source\b[^>]*>/i.exec(inner ?? '')?.[0] ?? '', 'src');
|
|
40
|
+
const title = tagAttr(tag, 'title');
|
|
41
|
+
return src ? `[video: ${title || 'recording'}](${src})\n` : '';
|
|
42
|
+
})
|
|
43
|
+
// An <img> carries meaning — the asset reference or URL — that the
|
|
44
|
+
// generic tag strip below would erase entirely. Keep it as a markdown
|
|
45
|
+
// image so `task-asset:<id>` refs stay legible in a terminal (OSK-9856).
|
|
46
|
+
.replace(/<img\b[^>]*>/gi, (tag) => {
|
|
47
|
+
const src = tagAttr(tag, 'src');
|
|
48
|
+
const alt = tagAttr(tag, 'alt');
|
|
49
|
+
return src ? `` : '';
|
|
50
|
+
})
|
|
26
51
|
.replace(/<br\s*\/?>/gi, '\n')
|
|
27
52
|
.replace(/<\/(p|div|li|h[1-6])>/gi, '\n')
|
|
28
53
|
.replace(/<li[^>]*>/gi, ' - ')
|
|
@@ -76,6 +76,27 @@ export declare function describeTargetSync(target: {
|
|
|
76
76
|
updatedAt?: string;
|
|
77
77
|
driftCount?: number;
|
|
78
78
|
}, now?: number, liveDaemonIds?: string[]): string;
|
|
79
|
+
/**
|
|
80
|
+
* The whole error of every failed target, and what to do about it.
|
|
81
|
+
*
|
|
82
|
+
* The STATUS column caps at 78 characters, so a daemon error that names a path
|
|
83
|
+
* was cut mid-path — "must be inside a Git repository: /private/…" — and the
|
|
84
|
+
* table offered nothing past it: no remedy, and no way to learn that
|
|
85
|
+
* `instructions remove` was the way out (OSK-4626). The column keeps its cap;
|
|
86
|
+
* the full sentence and the next step go below the table, the way the re-bind
|
|
87
|
+
* commands do.
|
|
88
|
+
*
|
|
89
|
+
* The daemon retries a failed target on its next sync (every five minutes, or
|
|
90
|
+
* sooner on any change), so fixing the cause is itself a remedy and needs no
|
|
91
|
+
* command. A failed REMOVAL is the other case: it can only be abandoned when
|
|
92
|
+
* its daemon is gone, which is the one place `purge-target --force` belongs.
|
|
93
|
+
*/
|
|
94
|
+
export declare function failedTargetLines(targets: Array<{
|
|
95
|
+
id: string;
|
|
96
|
+
status?: string;
|
|
97
|
+
desiredState?: string;
|
|
98
|
+
lastError?: string;
|
|
99
|
+
}>, bin?: string): string[];
|
|
79
100
|
/**
|
|
80
101
|
* The command that re-binds a stalled target to the daemon now running on its
|
|
81
102
|
* machine, or null when no such daemon is live.
|
|
@@ -7,6 +7,7 @@ exports.resolveBundleLabel = resolveBundleLabel;
|
|
|
7
7
|
exports.parseDaemonId = parseDaemonId;
|
|
8
8
|
exports.sameMachineDaemon = sameMachineDaemon;
|
|
9
9
|
exports.describeTargetSync = describeTargetSync;
|
|
10
|
+
exports.failedTargetLines = failedTargetLines;
|
|
10
11
|
exports.rebindCommandFor = rebindCommandFor;
|
|
11
12
|
const promises_1 = require("node:fs/promises");
|
|
12
13
|
async function readStdin() {
|
|
@@ -188,6 +189,35 @@ function describeTargetSync(target, now = Date.now(), liveDaemonIds = []) {
|
|
|
188
189
|
}
|
|
189
190
|
return `${base} — stalled ${days}d; daemon may be gone: remove, then purge-target --force`;
|
|
190
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* The whole error of every failed target, and what to do about it.
|
|
194
|
+
*
|
|
195
|
+
* The STATUS column caps at 78 characters, so a daemon error that names a path
|
|
196
|
+
* was cut mid-path — "must be inside a Git repository: /private/…" — and the
|
|
197
|
+
* table offered nothing past it: no remedy, and no way to learn that
|
|
198
|
+
* `instructions remove` was the way out (OSK-4626). The column keeps its cap;
|
|
199
|
+
* the full sentence and the next step go below the table, the way the re-bind
|
|
200
|
+
* commands do.
|
|
201
|
+
*
|
|
202
|
+
* The daemon retries a failed target on its next sync (every five minutes, or
|
|
203
|
+
* sooner on any change), so fixing the cause is itself a remedy and needs no
|
|
204
|
+
* command. A failed REMOVAL is the other case: it can only be abandoned when
|
|
205
|
+
* its daemon is gone, which is the one place `purge-target --force` belongs.
|
|
206
|
+
*/
|
|
207
|
+
function failedTargetLines(targets, bin = 'skrr') {
|
|
208
|
+
const failed = targets.filter((target) => target.status === 'error');
|
|
209
|
+
if (failed.length === 0)
|
|
210
|
+
return [];
|
|
211
|
+
const lines = ['', 'Failed targets:'];
|
|
212
|
+
for (const target of failed) {
|
|
213
|
+
const removing = target.desiredState === 'removed';
|
|
214
|
+
lines.push(` ${target.id} — ${removing ? 'removal failed' : 'install failed'}: ${target.lastError || 'no reason reported'}`);
|
|
215
|
+
lines.push(removing
|
|
216
|
+
? ` Fix the cause and its daemon retries on the next sync. If that daemon is gone for good: \`${bin} instructions purge-target ${target.id} --force\``
|
|
217
|
+
: ` Fix the cause and its daemon retries on the next sync, or drop the target: \`${bin} instructions remove ${target.id}\``);
|
|
218
|
+
}
|
|
219
|
+
return lines;
|
|
220
|
+
}
|
|
191
221
|
/**
|
|
192
222
|
* The command that re-binds a stalled target to the daemon now running on its
|
|
193
223
|
* machine, or null when no such daemon is live.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* instruction-provenance.ts — tell a prompt's two version counters apart.
|
|
3
|
+
*
|
|
4
|
+
* A managed instruction prompt has its OWN version counter (1, 2, 3 — every
|
|
5
|
+
* saved body), and a prompt installed from a built-in template also carries
|
|
6
|
+
* the TEMPLATE release each body came from (1, 2, 5, 17 — skipping whatever an
|
|
7
|
+
* install never received). Every table labelled both `v`, and they agree only
|
|
8
|
+
* by coincidence: bundle v3 was built-in v5 on the installation that found this
|
|
9
|
+
* (dogfood OSK-4647), so "the preset went to v5" read as two versions behind.
|
|
10
|
+
* The provenance that settles it (`sourceTemplateKey`/`sourceTemplateVersion`)
|
|
11
|
+
* was reachable through `--json` alone.
|
|
12
|
+
*
|
|
13
|
+
* So the prompt's counter stays `v<n>`, and a template release is always
|
|
14
|
+
* written `built-in v<n>` — never a bare `v` that could be either.
|
|
15
|
+
*/
|
|
16
|
+
export interface TemplateProvenance {
|
|
17
|
+
sourceTemplateKey?: string;
|
|
18
|
+
sourceTemplateVersion?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `built-in v5` for a body taken from a built-in template, `built-in` when the
|
|
22
|
+
* row is recognised as one but its release was never derivable (the provenance
|
|
23
|
+
* backfill stamps the key without inventing a number), and `` for a body that
|
|
24
|
+
* was not taken from a template at all.
|
|
25
|
+
*/
|
|
26
|
+
export declare function builtInLabel(version: TemplateProvenance | null | undefined): string;
|
|
27
|
+
interface VersionLike extends TemplateProvenance {
|
|
28
|
+
versionNumber?: number;
|
|
29
|
+
}
|
|
30
|
+
interface BundleLike extends TemplateProvenance {
|
|
31
|
+
currentVersionNumber?: number;
|
|
32
|
+
currentVersion?: VersionLike | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The APPLIED cell of `instructions status`: `v3 (built-in v5)` when the
|
|
36
|
+
* applied body is the prompt's current one and that one came from a template.
|
|
37
|
+
* Only then is the release known without fetching the version history, so any
|
|
38
|
+
* other case says `v3` and nothing it cannot back.
|
|
39
|
+
*/
|
|
40
|
+
export declare function appliedVersionLabel(appliedVersionNumber: number | undefined, bundle: BundleLike | undefined): string;
|
|
41
|
+
/**
|
|
42
|
+
* One sentence for `instructions show`: which built-in template a prompt comes
|
|
43
|
+
* from, whether its current body IS a built-in release, and whether a newer
|
|
44
|
+
* release exists — the question no command answered before. `STATUS: available`
|
|
45
|
+
* on a version row means "not archived", not "newer than yours".
|
|
46
|
+
*
|
|
47
|
+
* `latestTemplateVersion` is the release this server would install today;
|
|
48
|
+
* undefined when it could not be read, in which case nothing is claimed about
|
|
49
|
+
* it. Returns null for a prompt that was not installed from a template.
|
|
50
|
+
*/
|
|
51
|
+
export declare function describeTemplateProvenance(bundle: BundleLike & {
|
|
52
|
+
currentVersionNumber: number;
|
|
53
|
+
}, latestTemplateVersion?: number): string | null;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* instruction-provenance.ts — tell a prompt's two version counters apart.
|
|
4
|
+
*
|
|
5
|
+
* A managed instruction prompt has its OWN version counter (1, 2, 3 — every
|
|
6
|
+
* saved body), and a prompt installed from a built-in template also carries
|
|
7
|
+
* the TEMPLATE release each body came from (1, 2, 5, 17 — skipping whatever an
|
|
8
|
+
* install never received). Every table labelled both `v`, and they agree only
|
|
9
|
+
* by coincidence: bundle v3 was built-in v5 on the installation that found this
|
|
10
|
+
* (dogfood OSK-4647), so "the preset went to v5" read as two versions behind.
|
|
11
|
+
* The provenance that settles it (`sourceTemplateKey`/`sourceTemplateVersion`)
|
|
12
|
+
* was reachable through `--json` alone.
|
|
13
|
+
*
|
|
14
|
+
* So the prompt's counter stays `v<n>`, and a template release is always
|
|
15
|
+
* written `built-in v<n>` — never a bare `v` that could be either.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.builtInLabel = builtInLabel;
|
|
19
|
+
exports.appliedVersionLabel = appliedVersionLabel;
|
|
20
|
+
exports.describeTemplateProvenance = describeTemplateProvenance;
|
|
21
|
+
/**
|
|
22
|
+
* `built-in v5` for a body taken from a built-in template, `built-in` when the
|
|
23
|
+
* row is recognised as one but its release was never derivable (the provenance
|
|
24
|
+
* backfill stamps the key without inventing a number), and `` for a body that
|
|
25
|
+
* was not taken from a template at all.
|
|
26
|
+
*/
|
|
27
|
+
function builtInLabel(version) {
|
|
28
|
+
if (!version?.sourceTemplateKey)
|
|
29
|
+
return '';
|
|
30
|
+
const release = version.sourceTemplateVersion;
|
|
31
|
+
return Number.isInteger(release) && Number(release) > 0 ? `built-in v${release}` : 'built-in';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The APPLIED cell of `instructions status`: `v3 (built-in v5)` when the
|
|
35
|
+
* applied body is the prompt's current one and that one came from a template.
|
|
36
|
+
* Only then is the release known without fetching the version history, so any
|
|
37
|
+
* other case says `v3` and nothing it cannot back.
|
|
38
|
+
*/
|
|
39
|
+
function appliedVersionLabel(appliedVersionNumber, bundle) {
|
|
40
|
+
if (!appliedVersionNumber)
|
|
41
|
+
return '-';
|
|
42
|
+
const base = `v${appliedVersionNumber}`;
|
|
43
|
+
const current = bundle?.currentVersion;
|
|
44
|
+
if (!current || current.versionNumber !== appliedVersionNumber)
|
|
45
|
+
return base;
|
|
46
|
+
const builtIn = builtInLabel(current);
|
|
47
|
+
return builtIn ? `${base} (${builtIn})` : base;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One sentence for `instructions show`: which built-in template a prompt comes
|
|
51
|
+
* from, whether its current body IS a built-in release, and whether a newer
|
|
52
|
+
* release exists — the question no command answered before. `STATUS: available`
|
|
53
|
+
* on a version row means "not archived", not "newer than yours".
|
|
54
|
+
*
|
|
55
|
+
* `latestTemplateVersion` is the release this server would install today;
|
|
56
|
+
* undefined when it could not be read, in which case nothing is claimed about
|
|
57
|
+
* it. Returns null for a prompt that was not installed from a template.
|
|
58
|
+
*/
|
|
59
|
+
function describeTemplateProvenance(bundle, latestTemplateVersion) {
|
|
60
|
+
if (!bundle.sourceTemplateKey)
|
|
61
|
+
return null;
|
|
62
|
+
const current = bundle.currentVersion;
|
|
63
|
+
const head = `From built-in template ${bundle.sourceTemplateKey}.`;
|
|
64
|
+
if (!current)
|
|
65
|
+
return head;
|
|
66
|
+
const release = current.sourceTemplateKey ? current.sourceTemplateVersion : undefined;
|
|
67
|
+
const version = `v${bundle.currentVersionNumber}`;
|
|
68
|
+
const latestKnown = Number.isInteger(latestTemplateVersion);
|
|
69
|
+
if (current.sourceTemplateKey) {
|
|
70
|
+
const builtIn = builtInLabel(current);
|
|
71
|
+
if (!latestKnown || !Number.isInteger(release))
|
|
72
|
+
return `${head} ${version} is ${builtIn}.`;
|
|
73
|
+
if (Number(latestTemplateVersion) > Number(release)) {
|
|
74
|
+
return `${head} ${version} is ${builtIn}; built-in v${latestTemplateVersion} is newer.`;
|
|
75
|
+
}
|
|
76
|
+
return `${head} ${version} is ${builtIn}, the latest.`;
|
|
77
|
+
}
|
|
78
|
+
// Newer releases are promoted only onto a prompt whose body is still a
|
|
79
|
+
// built-in one, so an edit is exactly when "is there a newer one" matters.
|
|
80
|
+
const edited = `${head} ${version} is your own edit, so newer built-in releases are not applied to it`;
|
|
81
|
+
return latestKnown
|
|
82
|
+
? `${edited}; the latest is built-in v${latestTemplateVersion}.`
|
|
83
|
+
: `${edited}.`;
|
|
84
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* is no `task-read` and adding one to make the table tidy would name a resource
|
|
8
8
|
* after an access mode.
|
|
9
9
|
*/
|
|
10
|
-
export type SessionTaskOperation = 'completion' | 'move' | 'comment' | 'event' | 'update' | 'result' | 'resume' | 'resumeRead' | 'deliverable' | 'review' | 'assessment' | 'read';
|
|
10
|
+
export type SessionTaskOperation = 'completion' | 'move' | 'comment' | 'event' | 'update' | 'result' | 'resume' | 'resumeRead' | 'deliverable' | 'review' | 'assessment' | 'attachment' | 'attachmentIntent' | 'attachmentComplete' | 'read';
|
|
11
11
|
/** Path suffix per operation. Exported for the parity test, not for callers. */
|
|
12
12
|
export declare const SESSION_TASK_PATH_SUFFIX: Record<SessionTaskOperation, string>;
|
|
13
13
|
/**
|
|
@@ -47,6 +47,9 @@ exports.SESSION_TASK_PATH_SUFFIX = {
|
|
|
47
47
|
deliverable: 'task-deliverables',
|
|
48
48
|
review: 'task-review',
|
|
49
49
|
assessment: 'task-expectation-assessment',
|
|
50
|
+
attachment: 'task-attachments',
|
|
51
|
+
attachmentIntent: 'task-attachments/intent',
|
|
52
|
+
attachmentComplete: 'task-attachments/complete',
|
|
50
53
|
read: 'task',
|
|
51
54
|
};
|
|
52
55
|
/**
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type TaskAssetRole = 'input' | 'evidence' | 'deliverable';
|
|
2
|
+
export interface UploadedTaskAsset {
|
|
3
|
+
id: string;
|
|
4
|
+
/**
|
|
5
|
+
* sha256 (hex) of the bytes the server STORED, as the server recorded them.
|
|
6
|
+
* Absent when the server did not report one — never back-filled from the
|
|
7
|
+
* local file, which is a different version for any canonicalized image.
|
|
8
|
+
*/
|
|
9
|
+
digest?: string;
|
|
10
|
+
filename: string;
|
|
11
|
+
mimeType: string;
|
|
12
|
+
/** True when the server returned an existing row for the same sha256. */
|
|
13
|
+
deduplicated: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** The citation an Update or Result appends to `evidenceRefs`. */
|
|
16
|
+
export interface TaskAssetEvidenceRef {
|
|
17
|
+
kind: 'task_attachment';
|
|
18
|
+
id: string;
|
|
19
|
+
/** `sha256:<hex>` of the stored bytes; omitted when the server named none. */
|
|
20
|
+
digest?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the Task an upload targets, through the same credential the upload
|
|
24
|
+
* will use. In a session the task comes from the claim, so the read is the
|
|
25
|
+
* bound task and `taskId` is only reconciled against it — a scoped credential
|
|
26
|
+
* refuses to act on any other task before a byte leaves the machine.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readTaskForUpload(taskId: string): Promise<Record<string, unknown>>;
|
|
29
|
+
/**
|
|
30
|
+
* Upload `filePath` as a Task asset. In a session the task comes from the
|
|
31
|
+
* claim — `taskId` is only reconciled against the binding, never sent.
|
|
32
|
+
* Throws a plain Error; callers decide how to surface it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function uploadTaskAssetFile(opts: {
|
|
35
|
+
taskId: string;
|
|
36
|
+
filePath: string;
|
|
37
|
+
role?: TaskAssetRole;
|
|
38
|
+
}): Promise<UploadedTaskAsset>;
|
|
39
|
+
/**
|
|
40
|
+
* Upload every `--attach` file and return the reference objects an Update or
|
|
41
|
+
* Result appends to `evidenceRefs`. Serial: half a dozen files is the
|
|
42
|
+
* realistic case and parallel uploads would race the session rate limiter.
|
|
43
|
+
*/
|
|
44
|
+
export declare function attachTaskAssetFiles(opts: {
|
|
45
|
+
taskId: string;
|
|
46
|
+
files: string[];
|
|
47
|
+
role: TaskAssetRole;
|
|
48
|
+
}): Promise<TaskAssetEvidenceRef[]>;
|
|
49
|
+
/**
|
|
50
|
+
* The markdown image a comment body carries for an asset. The alt text is the
|
|
51
|
+
* filename, so its brackets are escaped: `shot [final].png` would otherwise
|
|
52
|
+
* close the alt early and break the reference.
|
|
53
|
+
*/
|
|
54
|
+
export declare function taskAssetMarkdownRef(asset: {
|
|
55
|
+
id: string;
|
|
56
|
+
filename: string;
|
|
57
|
+
}): string;
|
|
58
|
+
/** sha256 of a file, read as a stream: a 250MB recording never sits in memory. */
|
|
59
|
+
export declare function sha256OfFile(filePath: string): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* POST the file to the presigned URL as multipart/form-data, streamed from
|
|
62
|
+
* disk.
|
|
63
|
+
*
|
|
64
|
+
* Built by hand rather than with `FormData` + `fetch` for two reasons, both
|
|
65
|
+
* about large files. `FormData` needs a `Blob`, which here means the whole
|
|
66
|
+
* file in memory — the double buffering this replaced read up to 250MB and
|
|
67
|
+
* then copied it again. And storage refuses a POST without a Content-Length
|
|
68
|
+
* (it does not take chunked uploads), which a streamed `fetch` body cannot
|
|
69
|
+
* promise; the exact length is known here, because every part except the file
|
|
70
|
+
* is a small in-memory string and the file's size came from `stat`.
|
|
71
|
+
*
|
|
72
|
+
* `file` goes LAST — S3 presigned POST evaluates fields in order and ignores
|
|
73
|
+
* anything after it. The fields carry `x-amz-checksum-sha256`, so storage
|
|
74
|
+
* itself rejects bytes that do not match the digest the intent declared,
|
|
75
|
+
* including a file that changed between hashing and upload.
|
|
76
|
+
*/
|
|
77
|
+
export declare function postFileToStorage(p: {
|
|
78
|
+
uploadUrl: string;
|
|
79
|
+
uploadFields: Record<string, string>;
|
|
80
|
+
filePath: string;
|
|
81
|
+
size: number;
|
|
82
|
+
filename: string;
|
|
83
|
+
mimeType: string;
|
|
84
|
+
}): Promise<void>;
|