@skrr-ai/cli 0.1.37 → 0.1.38
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/bin/dev-fallback.js +167 -4
- package/dist/base-command.js +46 -2
- package/dist/commands/agents/actions/create.js +18 -7
- package/dist/commands/agents/actions/delete.d.ts +1 -0
- package/dist/commands/agents/actions/delete.js +7 -0
- package/dist/commands/agents/actions/update.js +5 -3
- package/dist/commands/agents/api-actions/add.js +5 -2
- package/dist/commands/agents/api-actions/index.d.ts +11 -0
- package/dist/commands/agents/api-actions/index.js +27 -1
- package/dist/commands/agents/avatar/default/index.d.ts +11 -0
- package/dist/commands/agents/avatar/default/index.js +17 -0
- package/dist/commands/agents/avatar/default/set.d.ts +2 -1
- package/dist/commands/agents/avatar/default/set.js +32 -7
- package/dist/commands/agents/avatar/upload.js +7 -1
- package/dist/commands/agents/categories/delete.js +8 -0
- package/dist/commands/agents/categories/index.d.ts +21 -5
- package/dist/commands/agents/categories/index.js +68 -15
- package/dist/commands/agents/chat.d.ts +21 -0
- package/dist/commands/agents/chat.js +94 -7
- package/dist/commands/agents/create.js +11 -4
- package/dist/commands/agents/list.d.ts +15 -0
- package/dist/commands/agents/list.js +31 -1
- package/dist/commands/agents/scene-background/upload.d.ts +30 -0
- package/dist/commands/agents/scene-background/upload.js +97 -0
- package/dist/commands/agents/show.js +13 -0
- package/dist/commands/agents/tools/calls.d.ts +23 -5
- package/dist/commands/agents/tools/calls.js +81 -18
- package/dist/commands/agents/update.js +15 -5
- package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
- package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
- package/dist/commands/convos/messages.d.ts +13 -0
- package/dist/commands/convos/messages.js +40 -16
- package/dist/commands/daemon/index.js +8 -1
- package/dist/commands/daemon/logs.d.ts +26 -0
- package/dist/commands/daemon/logs.js +56 -0
- package/dist/commands/daemon/uninstall.js +1 -1
- package/dist/commands/followups/act.js +3 -2
- package/dist/commands/followups/cancel.d.ts +6 -3
- package/dist/commands/followups/cancel.js +16 -6
- package/dist/commands/followups/observability.d.ts +23 -0
- package/dist/commands/followups/observability.js +114 -0
- package/dist/commands/followups/reschedule.js +1 -1
- package/dist/commands/followups/resolve.js +3 -3
- package/dist/commands/followups/show.js +2 -1
- package/dist/commands/followups/watch.js +9 -5
- package/dist/commands/harnesses/forget.js +28 -0
- package/dist/commands/harnesses/install.d.ts +41 -0
- package/dist/commands/harnesses/install.js +165 -0
- package/dist/commands/harnesses/installers.d.ts +45 -0
- package/dist/commands/harnesses/installers.js +155 -0
- package/dist/commands/harnesses/list.d.ts +82 -1
- package/dist/commands/harnesses/list.js +104 -12
- package/dist/commands/harnesses/models.js +10 -1
- package/dist/commands/harnesses/show.d.ts +15 -0
- package/dist/commands/harnesses/show.js +44 -3
- package/dist/commands/harnesses/update.js +1 -1
- package/dist/commands/harnesses/usage.d.ts +25 -0
- package/dist/commands/harnesses/usage.js +24 -2
- package/dist/commands/instructions/status.js +13 -1
- package/dist/commands/machines/dedicated/catalog.js +4 -15
- package/dist/commands/machines/dedicated/create.d.ts +8 -0
- package/dist/commands/machines/dedicated/create.js +58 -1
- package/dist/commands/machines/dedicated/destroy.js +18 -4
- package/dist/commands/machines/dedicated/list.js +9 -6
- package/dist/commands/machines/hosted/list.js +12 -0
- package/dist/commands/machines/list.js +11 -0
- package/dist/commands/machines/show.d.ts +1 -0
- package/dist/commands/machines/show.js +40 -11
- package/dist/commands/profiles/list.d.ts +23 -0
- package/dist/commands/profiles/list.js +96 -0
- package/dist/commands/search/skills.d.ts +11 -1
- package/dist/commands/search/skills.js +26 -8
- package/dist/commands/skills/details.d.ts +2 -0
- package/dist/commands/skills/details.js +24 -1
- package/dist/commands/skills/import-as-actions.d.ts +32 -0
- package/dist/commands/skills/import-as-actions.js +175 -0
- package/dist/commands/skills/install.d.ts +2 -0
- package/dist/commands/skills/install.js +24 -1
- package/dist/commands/skills/invocable.js +6 -2
- package/dist/commands/skills/local.d.ts +40 -0
- package/dist/commands/skills/local.js +160 -0
- package/dist/commands/skills/search.d.ts +11 -1
- package/dist/commands/skills/search.js +29 -8
- package/dist/commands/spaces/actions/add.js +23 -5
- package/dist/commands/spaces/actions/available.js +3 -0
- package/dist/commands/spaces/actions/list.d.ts +5 -0
- package/dist/commands/spaces/actions/list.js +49 -3
- package/dist/commands/spaces/actions/run.d.ts +28 -0
- package/dist/commands/spaces/actions/run.js +146 -0
- package/dist/commands/spaces/actions/update.js +27 -5
- package/dist/commands/spaces/label-summaries.d.ts +27 -0
- package/dist/commands/spaces/label-summaries.js +64 -0
- package/dist/commands/spaces/task-rollups.d.ts +5 -0
- package/dist/commands/spaces/task-rollups.js +15 -4
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
- package/dist/commands/tasks/actions/attach.js +32 -7
- package/dist/commands/tasks/complete.js +13 -0
- package/dist/commands/tasks/create.js +1 -1
- package/dist/commands/tasks/events/append.js +29 -6
- package/dist/commands/tasks/expectations/assess.d.ts +1 -0
- package/dist/commands/tasks/expectations/assess.js +43 -5
- package/dist/commands/tasks/expectations.js +77 -16
- package/dist/commands/tasks/follow-up.js +2 -2
- package/dist/commands/tasks/lifecycle/definitions.js +21 -2
- package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
- package/dist/commands/tasks/lifecycle/detach.js +43 -3
- package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/migrate.js +42 -3
- package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
- package/dist/commands/tasks/lifecycle/reopen.js +40 -3
- package/dist/commands/tasks/lifecycle/transition.js +33 -2
- package/dist/commands/tasks/list.js +16 -9
- package/dist/commands/tasks/move.js +11 -13
- package/dist/commands/tasks/review-queue.d.ts +15 -0
- package/dist/commands/tasks/review-queue.js +180 -31
- package/dist/commands/tasks/show.d.ts +11 -0
- package/dist/commands/tasks/show.js +105 -12
- package/dist/commands/tasks/trust.js +7 -2
- package/dist/commands/tasks/update.js +46 -10
- package/dist/commands/tasks/updates/add.js +1 -1
- package/dist/commands/token/list.d.ts +22 -0
- package/dist/commands/token/list.js +68 -0
- package/dist/commands/token/revoke.d.ts +23 -0
- package/dist/commands/token/revoke.js +53 -0
- package/dist/lib/agent-actions.d.ts +57 -2
- package/dist/lib/agent-actions.js +113 -21
- package/dist/lib/agent-pin-mute.d.ts +10 -5
- package/dist/lib/agent-pin-mute.js +17 -4
- package/dist/lib/agent-visual-refs.d.ts +27 -0
- package/dist/lib/agent-visual-refs.js +127 -2
- package/dist/lib/agentic-stream.d.ts +6 -0
- package/dist/lib/agentic-stream.js +34 -3
- package/dist/lib/assignee-resolver.d.ts +1 -1
- package/dist/lib/assignee-resolver.js +8 -0
- package/dist/lib/bulk-task-targets.d.ts +12 -0
- package/dist/lib/bulk-task-targets.js +34 -1
- package/dist/lib/cli-installers.d.ts +130 -0
- package/dist/lib/cli-installers.js +145 -0
- package/dist/lib/command-miss.js +55 -7
- package/dist/lib/dedicated-lease-command.d.ts +13 -0
- package/dist/lib/dedicated-lease-command.js +22 -1
- package/dist/lib/dedicated-machines.d.ts +95 -1
- package/dist/lib/dedicated-machines.js +236 -2
- package/dist/lib/failed-lookup.d.ts +3 -0
- package/dist/lib/failed-lookup.js +40 -0
- package/dist/lib/first-party-harness-agent.js +9 -2
- package/dist/lib/first-party-harness-doctor.d.ts +15 -0
- package/dist/lib/first-party-harness-doctor.js +126 -27
- package/dist/lib/followups.d.ts +14 -6
- package/dist/lib/followups.js +31 -6
- package/dist/lib/harnesses.d.ts +8 -0
- package/dist/lib/instruction-input.d.ts +19 -1
- package/dist/lib/instruction-input.js +52 -1
- package/dist/lib/label-scope.d.ts +15 -0
- package/dist/lib/label-scope.js +25 -5
- package/dist/lib/local-skills.d.ts +98 -0
- package/dist/lib/local-skills.js +195 -0
- package/dist/lib/machines.d.ts +10 -10
- package/dist/lib/machines.js +23 -1
- package/dist/lib/search-query.d.ts +19 -0
- package/dist/lib/search-query.js +35 -0
- package/dist/lib/space-custom-actions.d.ts +72 -0
- package/dist/lib/space-custom-actions.js +117 -0
- package/dist/lib/space-resolver.d.ts +1 -21
- package/dist/lib/space-resolver.js +19 -10
- package/dist/lib/task-closure.d.ts +3 -2
- package/dist/lib/task-closure.js +18 -20
- package/dist/lib/task-lifecycle-output.js +117 -9
- package/dist/lib/task-ref-resolver.d.ts +1 -17
- package/dist/lib/task-ref-resolver.js +18 -8
- package/dist/lib/tasks.js +9 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
- package/oclif.manifest.json +26635 -25649
- package/package.json +9 -3
package/dist/lib/followups.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.followupsApi = exports.WATCH_WAIT_KINDS = exports.FOLLOWUP_RESOLVE_OUTCOMES = exports.FOLLOWUP_STATES = exports.FOLLOWUP_KINDS = void 0;
|
|
3
|
+
exports.followupsApi = exports.WATCH_WAIT_KINDS = exports.FOLLOWUP_RESOLVE_OUTCOMES = exports.FOLLOWUP_STATES = exports.FOLLOWUP_KINDS = exports.followUpIdArg = void 0;
|
|
4
4
|
exports.resolveFollowUpAgent = resolveFollowUpAgent;
|
|
5
5
|
exports.parseSubjectRef = parseSubjectRef;
|
|
6
6
|
exports.followupSummaryLine = followupSummaryLine;
|
|
@@ -19,19 +19,44 @@ exports.followupSummaryLine = followupSummaryLine;
|
|
|
19
19
|
* `why` and the server derives the rest; a second derivation here would be
|
|
20
20
|
* exactly the drift the service exists to kill.
|
|
21
21
|
*/
|
|
22
|
+
const core_1 = require("@oclif/core");
|
|
22
23
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
23
24
|
const runtime_context_1 = require("./runtime-context");
|
|
24
25
|
const triggers_1 = require("./triggers");
|
|
25
26
|
const agent_resolver_1 = require("./agent-resolver");
|
|
26
27
|
const MOUNT = '/api/followups';
|
|
27
28
|
const base = (id) => `${MOUNT}/${encodeURIComponent(id)}`;
|
|
29
|
+
/**
|
|
30
|
+
* The `id` argument every follow-up command that addresses one takes.
|
|
31
|
+
*
|
|
32
|
+
* oclif treats an empty string as a PRESENT required argument, so a scripted
|
|
33
|
+
* `skrr followups show "$ID"` with an empty variable built `/api/followups/` —
|
|
34
|
+
* the LIST route — and answered with the list's requirement, "agentId is
|
|
35
|
+
* required unless allAgents is set", naming two flags `show` does not have
|
|
36
|
+
* (OSK-9676). Refused at parse time instead, so it renders where a missing
|
|
37
|
+
* argument does, with usage, before any request is sent.
|
|
38
|
+
*/
|
|
39
|
+
exports.followUpIdArg = core_1.Args.string({
|
|
40
|
+
description: 'Follow-up (trigger) id',
|
|
41
|
+
required: true,
|
|
42
|
+
ignoreStdin: true,
|
|
43
|
+
parse: async (input) => {
|
|
44
|
+
const id = input.trim();
|
|
45
|
+
if (!id)
|
|
46
|
+
throw new Error('id is empty — pass the follow-up id (see `skrr followups list`)');
|
|
47
|
+
return id;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
28
50
|
/* ------------------------------------------------------------------ *
|
|
29
51
|
* Vocabulary (client-side validation only — the server is the authority) *
|
|
30
52
|
* ------------------------------------------------------------------ */
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
// Owned by `@skrr-ai/data-provider`, which the server validates against. These
|
|
54
|
+
// were declared a second time here, and the state list is where the two copies
|
|
55
|
+
// disagreed.
|
|
56
|
+
var data_provider_2 = require("@skrr-ai/data-provider");
|
|
57
|
+
Object.defineProperty(exports, "FOLLOWUP_KINDS", { enumerable: true, get: function () { return data_provider_2.FOLLOWUP_KINDS; } });
|
|
58
|
+
Object.defineProperty(exports, "FOLLOWUP_STATES", { enumerable: true, get: function () { return data_provider_2.FOLLOWUP_STATES; } });
|
|
59
|
+
Object.defineProperty(exports, "FOLLOWUP_RESOLVE_OUTCOMES", { enumerable: true, get: function () { return data_provider_2.FOLLOWUP_RESOLVE_OUTCOMES; } });
|
|
35
60
|
exports.WATCH_WAIT_KINDS = ['clock', 'event', 'poll_until'];
|
|
36
61
|
/**
|
|
37
62
|
* Which agent a follow-up belongs to.
|
|
@@ -65,7 +90,7 @@ function resolveFollowUpAgent(explicitAgentId, env = process.env) {
|
|
|
65
90
|
return { id: explicitAgentId, boundToSession: false };
|
|
66
91
|
throw new agent_resolver_1.AgentResolutionError({
|
|
67
92
|
code: 'AGENT_CONTEXT_AMBIGUOUS',
|
|
68
|
-
message: 'No agent
|
|
93
|
+
message: 'No agent: this session is not bound to one, so name the agent these follow-ups are for.',
|
|
69
94
|
suggestion: 'Pass `--agent <agent-id>`, run inside a daemon session that sets OVERSKY_SESSION_AGENT_ID, or use `--all-agents` where listing is the intent.',
|
|
70
95
|
details: { runtimeKind: runtime.kind },
|
|
71
96
|
});
|
package/dist/lib/harnesses.d.ts
CHANGED
|
@@ -42,6 +42,14 @@ export interface Harness {
|
|
|
42
42
|
_id: string;
|
|
43
43
|
provider: string;
|
|
44
44
|
name?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The name everyone else sees, when YOUR alias is covering it. `name` is the
|
|
47
|
+
* effective one. Present only while an alias is actually shadowing a
|
|
48
|
+
* different value, so a reader can tell "no alias" from "alias equals the
|
|
49
|
+
* shared name".
|
|
50
|
+
*/
|
|
51
|
+
sharedName?: string;
|
|
52
|
+
aliased?: boolean;
|
|
45
53
|
status?: string;
|
|
46
54
|
harnessLocation?: 'local' | 'cloud';
|
|
47
55
|
daemonId?: string;
|
|
@@ -50,11 +50,29 @@ export declare function resolveBundleLabel(bundleId: string | undefined, names:
|
|
|
50
50
|
* the daemon is genuinely gone. A remedy that 409s is worse than no remedy — it
|
|
51
51
|
* spends the reader's attention and returns them to where they started.
|
|
52
52
|
*/
|
|
53
|
+
/**
|
|
54
|
+
* A daemon id is `daemon_<hash>_<source>[_<profile>]`, where the hash covers
|
|
55
|
+
* the MACHINE and the cwd and `<source>` is the supervision mode
|
|
56
|
+
* (`cli`, `launchd`, `electron`, …). So two ids that differ only in that
|
|
57
|
+
* segment describe the SAME machine and directory, started a different way.
|
|
58
|
+
*
|
|
59
|
+
* That matters because switching how the daemon is started — the desktop app
|
|
60
|
+
* installs a launchd agent, `skrr daemon start` runs a cli one — mints a new
|
|
61
|
+
* id and orphans every managed-instruction target pinned to the old one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseDaemonId(daemonId: string): {
|
|
64
|
+
place: string;
|
|
65
|
+
source: string;
|
|
66
|
+
profile: string;
|
|
67
|
+
} | null;
|
|
68
|
+
/** The live daemon that is this target's machine and directory, started differently. */
|
|
69
|
+
export declare function sameMachineDaemon(targetDaemonId: string, liveDaemonIds: string[]): string | null;
|
|
53
70
|
export declare function describeTargetSync(target: {
|
|
54
71
|
status: string;
|
|
72
|
+
daemonId?: string;
|
|
55
73
|
lastError?: string;
|
|
56
74
|
lastSyncedAt?: string;
|
|
57
75
|
lastAttemptedAt?: string;
|
|
58
76
|
updatedAt?: string;
|
|
59
77
|
driftCount?: number;
|
|
60
|
-
}, now?: number): string;
|
|
78
|
+
}, now?: number, liveDaemonIds?: string[]): string;
|
|
@@ -4,6 +4,8 @@ exports.readInstructionBody = readInstructionBody;
|
|
|
4
4
|
exports.formatSyncStatus = formatSyncStatus;
|
|
5
5
|
exports.buildBundleNameIndex = buildBundleNameIndex;
|
|
6
6
|
exports.resolveBundleLabel = resolveBundleLabel;
|
|
7
|
+
exports.parseDaemonId = parseDaemonId;
|
|
8
|
+
exports.sameMachineDaemon = sameMachineDaemon;
|
|
7
9
|
exports.describeTargetSync = describeTargetSync;
|
|
8
10
|
const promises_1 = require("node:fs/promises");
|
|
9
11
|
async function readStdin() {
|
|
@@ -93,7 +95,39 @@ const PENDING_IS_SUSPECT_AFTER_MS = 24 * 60 * 60 * 1000;
|
|
|
93
95
|
* the daemon is genuinely gone. A remedy that 409s is worse than no remedy — it
|
|
94
96
|
* spends the reader's attention and returns them to where they started.
|
|
95
97
|
*/
|
|
96
|
-
|
|
98
|
+
/**
|
|
99
|
+
* A daemon id is `daemon_<hash>_<source>[_<profile>]`, where the hash covers
|
|
100
|
+
* the MACHINE and the cwd and `<source>` is the supervision mode
|
|
101
|
+
* (`cli`, `launchd`, `electron`, …). So two ids that differ only in that
|
|
102
|
+
* segment describe the SAME machine and directory, started a different way.
|
|
103
|
+
*
|
|
104
|
+
* That matters because switching how the daemon is started — the desktop app
|
|
105
|
+
* installs a launchd agent, `skrr daemon start` runs a cli one — mints a new
|
|
106
|
+
* id and orphans every managed-instruction target pinned to the old one.
|
|
107
|
+
*/
|
|
108
|
+
function parseDaemonId(daemonId) {
|
|
109
|
+
const parts = String(daemonId || '').split('_');
|
|
110
|
+
if (parts.length < 3 || parts[0] !== 'daemon')
|
|
111
|
+
return null;
|
|
112
|
+
return { place: parts[1], source: parts[2], profile: parts.slice(3).join('_') };
|
|
113
|
+
}
|
|
114
|
+
/** The live daemon that is this target's machine and directory, started differently. */
|
|
115
|
+
function sameMachineDaemon(targetDaemonId, liveDaemonIds) {
|
|
116
|
+
const target = parseDaemonId(targetDaemonId);
|
|
117
|
+
if (!target)
|
|
118
|
+
return null;
|
|
119
|
+
for (const live of liveDaemonIds) {
|
|
120
|
+
if (live === targetDaemonId)
|
|
121
|
+
continue;
|
|
122
|
+
const other = parseDaemonId(live);
|
|
123
|
+
if (!other)
|
|
124
|
+
continue;
|
|
125
|
+
if (other.place === target.place && other.profile === target.profile)
|
|
126
|
+
return live;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function describeTargetSync(target, now = Date.now(), liveDaemonIds = []) {
|
|
97
131
|
const base = formatSyncStatus(target.status, target.lastError);
|
|
98
132
|
// Say when skrr has overwritten something you typed.
|
|
99
133
|
//
|
|
@@ -131,5 +165,22 @@ function describeTargetSync(target, now = Date.now()) {
|
|
|
131
165
|
// remedy off the end — the same truncation e51abf3362 exists to prevent. A
|
|
132
166
|
// stalled target is not reconciling, so its drift is historical and the
|
|
133
167
|
// remedy is the actionable half; the count reappears once it is unstuck.
|
|
168
|
+
// CHECK BEFORE ASSERTING, and never ship `--force` with a guess.
|
|
169
|
+
//
|
|
170
|
+
// "daemon may be gone" was stated unconditionally. On this machine it was
|
|
171
|
+
// false — the daemon had answered a heartbeat 112 ms earlier — because the
|
|
172
|
+
// target is pinned to `daemon_<hash>_launchd_default` while the live daemon
|
|
173
|
+
// is `daemon_<hash>_cli_default`: same machine, same directory, started a
|
|
174
|
+
// different way. So the CLI confidently recommended
|
|
175
|
+
// `remove, then purge-target --force` against live, correctly-installed
|
|
176
|
+
// managed blocks. An error that is confidently wrong AND carries its own
|
|
177
|
+
// `--force` is worse than one that says it does not know.
|
|
178
|
+
const elsewhere = sameMachineDaemon(target.daemonId || '', liveDaemonIds);
|
|
179
|
+
if (elsewhere) {
|
|
180
|
+
const live = parseDaemonId(elsewhere);
|
|
181
|
+
const pinned = parseDaemonId(target.daemonId || '');
|
|
182
|
+
// Kept under the STATUS column's `maxWidth: 78`, same constraint as below.
|
|
183
|
+
return `${base} — stalled ${days}d; daemon here now runs as ${live?.source}, not ${pinned?.source}: re-install`;
|
|
184
|
+
}
|
|
134
185
|
return `${base} — stalled ${days}d; daemon may be gone: remove, then purge-target --force`;
|
|
135
186
|
}
|
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export declare const LABEL_SCOPES: readonly ["user", "workspace"];
|
|
13
13
|
export type LabelScope = (typeof LABEL_SCOPES)[number];
|
|
14
|
+
/**
|
|
15
|
+
* Scopes whose id the server cannot infer, so `--scope-id` must name it.
|
|
16
|
+
*
|
|
17
|
+
* `resolveLabelScope` below and the `--scope-id` help text are BOTH derived
|
|
18
|
+
* from this, rather than each restating the list. They drifted once already:
|
|
19
|
+
* `0298b7e4fd` collapsed the vocabulary from three scopes to two, and the help
|
|
20
|
+
* kept advertising `--scope space` for months after the validator had stopped
|
|
21
|
+
* accepting it — so following `--help` produced `Expected --scope=space to be
|
|
22
|
+
* one of: user, workspace`, whose last line points back at the help that was
|
|
23
|
+
* wrong. Static prose describing a value set is how that happens; deriving it
|
|
24
|
+
* is what stops the next collapse doing the same.
|
|
25
|
+
*/
|
|
26
|
+
export declare const LABEL_SCOPES_REQUIRING_ID: readonly ["workspace"];
|
|
27
|
+
/** Scopes the server resolves from the caller — passing an id is an error. */
|
|
28
|
+
export declare const LABEL_SCOPES_INFERRED: "user"[];
|
|
14
29
|
export declare const LABEL_APPLICABLE: readonly ["task", "space", "goal", "routine"];
|
|
15
30
|
export type LabelApplicable = (typeof LABEL_APPLICABLE)[number];
|
|
16
31
|
/**
|
package/dist/lib/label-scope.js
CHANGED
|
@@ -11,12 +11,31 @@
|
|
|
11
11
|
* id the server can infer (it falls back to the caller).
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.labelScopeFlags = exports.HEX_COLOR_RE = exports.LABEL_APPLICABLE = exports.LABEL_SCOPES = void 0;
|
|
14
|
+
exports.labelScopeFlags = exports.HEX_COLOR_RE = exports.LABEL_APPLICABLE = exports.LABEL_SCOPES_INFERRED = exports.LABEL_SCOPES_REQUIRING_ID = exports.LABEL_SCOPES = void 0;
|
|
15
15
|
exports.normalizeLabelColor = normalizeLabelColor;
|
|
16
16
|
exports.resolveLabelScope = resolveLabelScope;
|
|
17
17
|
exports.resolveApplicableTo = resolveApplicableTo;
|
|
18
18
|
const core_1 = require("@oclif/core");
|
|
19
19
|
exports.LABEL_SCOPES = ['user', 'workspace'];
|
|
20
|
+
/**
|
|
21
|
+
* Scopes whose id the server cannot infer, so `--scope-id` must name it.
|
|
22
|
+
*
|
|
23
|
+
* `resolveLabelScope` below and the `--scope-id` help text are BOTH derived
|
|
24
|
+
* from this, rather than each restating the list. They drifted once already:
|
|
25
|
+
* `0298b7e4fd` collapsed the vocabulary from three scopes to two, and the help
|
|
26
|
+
* kept advertising `--scope space` for months after the validator had stopped
|
|
27
|
+
* accepting it — so following `--help` produced `Expected --scope=space to be
|
|
28
|
+
* one of: user, workspace`, whose last line points back at the help that was
|
|
29
|
+
* wrong. Static prose describing a value set is how that happens; deriving it
|
|
30
|
+
* is what stops the next collapse doing the same.
|
|
31
|
+
*/
|
|
32
|
+
exports.LABEL_SCOPES_REQUIRING_ID = ['workspace'];
|
|
33
|
+
const scopesRequiringId = () => exports.LABEL_SCOPES_REQUIRING_ID;
|
|
34
|
+
/** Scopes the server resolves from the caller — passing an id is an error. */
|
|
35
|
+
exports.LABEL_SCOPES_INFERRED = exports.LABEL_SCOPES.filter((scope) => !exports.LABEL_SCOPES_REQUIRING_ID.includes(scope));
|
|
36
|
+
function joinScopes(scopes) {
|
|
37
|
+
return scopes.map((scope) => `--scope ${scope}`).join(' / ');
|
|
38
|
+
}
|
|
20
39
|
exports.LABEL_APPLICABLE = ['task', 'space', 'goal', 'routine'];
|
|
21
40
|
/**
|
|
22
41
|
* The server stores colors as bare 6-hex (`api/server/routes/labels.js` Zod is
|
|
@@ -34,7 +53,7 @@ exports.labelScopeFlags = {
|
|
|
34
53
|
default: 'user',
|
|
35
54
|
}),
|
|
36
55
|
'scope-id': core_1.Flags.string({
|
|
37
|
-
description:
|
|
56
|
+
description: `Scope owner ID — required for ${joinScopes(scopesRequiringId())}. Omit for ${joinScopes(exports.LABEL_SCOPES_INFERRED)} (defaults to you).`,
|
|
38
57
|
}),
|
|
39
58
|
};
|
|
40
59
|
/**
|
|
@@ -44,11 +63,12 @@ exports.labelScopeFlags = {
|
|
|
44
63
|
function resolveLabelScope(flags) {
|
|
45
64
|
const scope = (flags.scope ?? 'user');
|
|
46
65
|
const scopeId = flags['scope-id'];
|
|
47
|
-
|
|
66
|
+
const requiresId = exports.LABEL_SCOPES_REQUIRING_ID.includes(scope);
|
|
67
|
+
if (requiresId && !scopeId) {
|
|
48
68
|
throw new Error(`--scope-id is required for --scope ${scope}.`);
|
|
49
69
|
}
|
|
50
|
-
if (
|
|
51
|
-
throw new Error(
|
|
70
|
+
if (!requiresId && scopeId) {
|
|
71
|
+
throw new Error(`--scope-id is not accepted for --scope ${scope}; ${scope}-scope labels are always your own.`);
|
|
52
72
|
}
|
|
53
73
|
return { scope, ...(scopeId ? { scopeId } : {}) };
|
|
54
74
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { DaemonLocalSkill } from '@skrr-ai/data-provider';
|
|
2
|
+
/**
|
|
3
|
+
* Skills on a machine, as its daemon lists them — the CLI half of the `/` menu.
|
|
4
|
+
*
|
|
5
|
+
* `skrr` had two things called "skill" and commands for only one: store and
|
|
6
|
+
* workspace skills (`skills *`, `--skill <skill-id>`) had a full surface, while
|
|
7
|
+
* the skills in a machine's `.claude/skills` and sibling roots — the ones the
|
|
8
|
+
* web `/` menu offers — could be neither listed nor invoked, so a headless
|
|
9
|
+
* operator or a locally-running coding agent could not use them at all
|
|
10
|
+
* (OSK-9478). `skills local` lists them; `agents chat --local-skill` invokes
|
|
11
|
+
* one. Both resolve through here, so a name means the same skill in each.
|
|
12
|
+
*/
|
|
13
|
+
export type ConnectedDaemon = {
|
|
14
|
+
daemonId: string;
|
|
15
|
+
deviceName?: string | null;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Which machine to ask, or why there is no single answer.
|
|
19
|
+
*
|
|
20
|
+
* Resolves rather than guesses: picking one of several machines and reporting
|
|
21
|
+
* its skills as "the" answer is the same silent narrowing the listing exists
|
|
22
|
+
* to remove.
|
|
23
|
+
*/
|
|
24
|
+
export declare function chooseDaemon(active: ReadonlyArray<ConnectedDaemon>): {
|
|
25
|
+
daemonId: string;
|
|
26
|
+
} | {
|
|
27
|
+
problem: string;
|
|
28
|
+
candidates: ConnectedDaemon[];
|
|
29
|
+
};
|
|
30
|
+
/** A skill with no declared triggers is invocable both ways — the walker's default. */
|
|
31
|
+
export declare function isUserInvocable(skill: Pick<DaemonLocalSkill, 'triggers'>): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Turn requested names into the references the server resolves.
|
|
34
|
+
*
|
|
35
|
+
* A request is `name`, or `<root>/<name>` when the same name exists under more
|
|
36
|
+
* than one root (`.claude/skills/review` vs `.agents/skills/review`). Each
|
|
37
|
+
* problem is reported in words that say what to type instead; nothing is
|
|
38
|
+
* dropped, because the server's own rule is that a skill the user picked and
|
|
39
|
+
* the turn did not load must fail the turn rather than run without it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveLocalSkillRefs(inventory: ReadonlyArray<DaemonLocalSkill>, requested: ReadonlyArray<string>): {
|
|
42
|
+
refs: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
pathHash: string;
|
|
45
|
+
}>;
|
|
46
|
+
problems: string[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The prompt with a `/name` for every invoked skill it does not already name.
|
|
50
|
+
*
|
|
51
|
+
* The web composer sends the `/name` in the text beside the reference, and
|
|
52
|
+
* drops a reference whose `/name` is gone; a CLI turn carries the same pair so
|
|
53
|
+
* the agent reads the same invocation either way.
|
|
54
|
+
*/
|
|
55
|
+
export declare function withSkillInvocations(prompt: string, refs: ReadonlyArray<{
|
|
56
|
+
name: string;
|
|
57
|
+
}>): string;
|
|
58
|
+
/** Whether a skill argument names a place on disk rather than a store id. */
|
|
59
|
+
export declare function looksLikeSkillPath(arg: string): boolean;
|
|
60
|
+
export type LocalSkillSighting = {
|
|
61
|
+
daemonId: string;
|
|
62
|
+
deviceName?: string | null;
|
|
63
|
+
source?: string;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Where a skill with this name exists on the caller's connected machines.
|
|
67
|
+
*
|
|
68
|
+
* Best-effort by design: it runs only to word an error that is already going
|
|
69
|
+
* to be reported, so a machine that cannot be asked contributes nothing rather
|
|
70
|
+
* than replacing the real error with a second one.
|
|
71
|
+
*/
|
|
72
|
+
export declare function findLocalSkillsNamed(name: string): Promise<LocalSkillSighting[]>;
|
|
73
|
+
/**
|
|
74
|
+
* The error for a skill argument a STORE command could not use.
|
|
75
|
+
*
|
|
76
|
+
* `skills install <name>` and `skills details <name>` answered a local skill's
|
|
77
|
+
* name or path with "Skill not found / NOT_FOUND": true of the store, and no
|
|
78
|
+
* help to someone the product had just told it reads their `.claude/skills`
|
|
79
|
+
* (OSK-9412). The answer depends on what the argument was, so it says which:
|
|
80
|
+
* a path (never a store id), a name found on a connected machine (needs no
|
|
81
|
+
* install), or neither (look the id up in the store).
|
|
82
|
+
*/
|
|
83
|
+
export declare function describeStoreSkillMiss(arg: string, sightings: ReadonlyArray<LocalSkillSighting>, command: 'install' | 'details'): string;
|
|
84
|
+
/**
|
|
85
|
+
* The first daemon release whose walker scans the skill conventions — the
|
|
86
|
+
* project's `.claude/skills`, `.agents/skills`, `.devin/skills`,
|
|
87
|
+
* `.windsurf/skills` and the global `~/.claude/skills`. It arrived in
|
|
88
|
+
* `e9dfa732cd` and first shipped in 0.8.44; an older daemon scans only
|
|
89
|
+
* `claudeSession.skillsDir`, which is empty unless someone set it by hand.
|
|
90
|
+
*/
|
|
91
|
+
export declare const SKILL_CONVENTION_DISCOVERY_SINCE = "0.8.44";
|
|
92
|
+
/**
|
|
93
|
+
* Whether a daemon's reported version is older than `floor`.
|
|
94
|
+
*
|
|
95
|
+
* Unknown or unparseable versions answer false: this decides only whether to
|
|
96
|
+
* add an explanation, and claiming an unknown daemon is old would be a guess.
|
|
97
|
+
*/
|
|
98
|
+
export declare function daemonPredates(version: string | null | undefined, floor: string): boolean;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SKILL_CONVENTION_DISCOVERY_SINCE = void 0;
|
|
4
|
+
exports.chooseDaemon = chooseDaemon;
|
|
5
|
+
exports.isUserInvocable = isUserInvocable;
|
|
6
|
+
exports.resolveLocalSkillRefs = resolveLocalSkillRefs;
|
|
7
|
+
exports.withSkillInvocations = withSkillInvocations;
|
|
8
|
+
exports.looksLikeSkillPath = looksLikeSkillPath;
|
|
9
|
+
exports.findLocalSkillsNamed = findLocalSkillsNamed;
|
|
10
|
+
exports.describeStoreSkillMiss = describeStoreSkillMiss;
|
|
11
|
+
exports.daemonPredates = daemonPredates;
|
|
12
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
13
|
+
/**
|
|
14
|
+
* Which machine to ask, or why there is no single answer.
|
|
15
|
+
*
|
|
16
|
+
* Resolves rather than guesses: picking one of several machines and reporting
|
|
17
|
+
* its skills as "the" answer is the same silent narrowing the listing exists
|
|
18
|
+
* to remove.
|
|
19
|
+
*/
|
|
20
|
+
function chooseDaemon(active) {
|
|
21
|
+
if (active.length === 1)
|
|
22
|
+
return { daemonId: active[0].daemonId };
|
|
23
|
+
if (active.length === 0) {
|
|
24
|
+
return {
|
|
25
|
+
problem: 'No connected machine. Start one with `skrr daemon start`, then try again.',
|
|
26
|
+
candidates: [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
problem: `${active.length} machines are connected. Pick one with --daemon:`,
|
|
31
|
+
candidates: [...active],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** A skill with no declared triggers is invocable both ways — the walker's default. */
|
|
35
|
+
function isUserInvocable(skill) {
|
|
36
|
+
return !skill.triggers || skill.triggers.includes('user');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Turn requested names into the references the server resolves.
|
|
40
|
+
*
|
|
41
|
+
* A request is `name`, or `<root>/<name>` when the same name exists under more
|
|
42
|
+
* than one root (`.claude/skills/review` vs `.agents/skills/review`). Each
|
|
43
|
+
* problem is reported in words that say what to type instead; nothing is
|
|
44
|
+
* dropped, because the server's own rule is that a skill the user picked and
|
|
45
|
+
* the turn did not load must fail the turn rather than run without it.
|
|
46
|
+
*/
|
|
47
|
+
function resolveLocalSkillRefs(inventory, requested) {
|
|
48
|
+
const refs = [];
|
|
49
|
+
const problems = [];
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
for (const raw of requested) {
|
|
52
|
+
const request = raw.trim().replace(/^\//, '');
|
|
53
|
+
const slash = request.lastIndexOf('/');
|
|
54
|
+
const root = slash > 0 ? request.slice(0, slash) : null;
|
|
55
|
+
const name = slash > 0 ? request.slice(slash + 1) : request;
|
|
56
|
+
const named = inventory.filter((skill) => skill.name === name);
|
|
57
|
+
const matches = root ? named.filter((skill) => skill.source === root) : named;
|
|
58
|
+
if (matches.length === 0) {
|
|
59
|
+
problems.push(named.length > 0
|
|
60
|
+
? `No skill "${name}" under ${root}. It is under: ${named.map((s) => s.source ?? '?').join(', ')}.`
|
|
61
|
+
: `No skill named "${name}" on this machine. See \`skrr skills local\`.`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (matches.length > 1) {
|
|
65
|
+
problems.push(`"${name}" exists under ${matches.length} roots. Name one: ` +
|
|
66
|
+
matches.map((s) => `--local-skill ${s.source ?? '?'}/${name}`).join(' | '));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const [skill] = matches;
|
|
70
|
+
if (!isUserInvocable(skill)) {
|
|
71
|
+
problems.push(`"${name}" declares it is for the agent to reach for, not to invoke (triggers: model).`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (seen.has(skill.pathHash))
|
|
75
|
+
continue;
|
|
76
|
+
seen.add(skill.pathHash);
|
|
77
|
+
refs.push({ name: skill.name, pathHash: skill.pathHash });
|
|
78
|
+
}
|
|
79
|
+
return { refs, problems };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The prompt with a `/name` for every invoked skill it does not already name.
|
|
83
|
+
*
|
|
84
|
+
* The web composer sends the `/name` in the text beside the reference, and
|
|
85
|
+
* drops a reference whose `/name` is gone; a CLI turn carries the same pair so
|
|
86
|
+
* the agent reads the same invocation either way.
|
|
87
|
+
*/
|
|
88
|
+
function withSkillInvocations(prompt, refs) {
|
|
89
|
+
const missing = refs.filter((ref) => !new RegExp(`(^|\\s)/${ref.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=\\s|$)`).test(prompt));
|
|
90
|
+
if (missing.length === 0)
|
|
91
|
+
return prompt;
|
|
92
|
+
return `${missing.map((ref) => `/${ref.name}`).join(' ')} ${prompt}`;
|
|
93
|
+
}
|
|
94
|
+
/** Whether a skill argument names a place on disk rather than a store id. */
|
|
95
|
+
function looksLikeSkillPath(arg) {
|
|
96
|
+
return /[\\/]/.test(arg) || arg.startsWith('.') || arg.startsWith('~');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Where a skill with this name exists on the caller's connected machines.
|
|
100
|
+
*
|
|
101
|
+
* Best-effort by design: it runs only to word an error that is already going
|
|
102
|
+
* to be reported, so a machine that cannot be asked contributes nothing rather
|
|
103
|
+
* than replacing the real error with a second one.
|
|
104
|
+
*/
|
|
105
|
+
async function findLocalSkillsNamed(name) {
|
|
106
|
+
const bare = name.replace(/^\//, '');
|
|
107
|
+
let active = [];
|
|
108
|
+
try {
|
|
109
|
+
active = (await data_provider_1.dataService.getDaemons()).active ?? [];
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
const sightings = await Promise.all(active.map(async (daemon) => {
|
|
115
|
+
try {
|
|
116
|
+
const { skills } = await data_provider_1.dataService.getDaemonSkills(daemon.daemonId);
|
|
117
|
+
return (skills ?? [])
|
|
118
|
+
.filter((skill) => skill.name === bare)
|
|
119
|
+
.map((skill) => ({
|
|
120
|
+
daemonId: daemon.daemonId,
|
|
121
|
+
deviceName: daemon.deviceName,
|
|
122
|
+
source: skill.source,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
}));
|
|
129
|
+
return sightings.flat();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The error for a skill argument a STORE command could not use.
|
|
133
|
+
*
|
|
134
|
+
* `skills install <name>` and `skills details <name>` answered a local skill's
|
|
135
|
+
* name or path with "Skill not found / NOT_FOUND": true of the store, and no
|
|
136
|
+
* help to someone the product had just told it reads their `.claude/skills`
|
|
137
|
+
* (OSK-9412). The answer depends on what the argument was, so it says which:
|
|
138
|
+
* a path (never a store id), a name found on a connected machine (needs no
|
|
139
|
+
* install), or neither (look the id up in the store).
|
|
140
|
+
*/
|
|
141
|
+
function describeStoreSkillMiss(arg, sightings, command) {
|
|
142
|
+
const verb = command === 'install' ? 'installs' : 'shows';
|
|
143
|
+
if (looksLikeSkillPath(arg)) {
|
|
144
|
+
return [
|
|
145
|
+
`\`skills ${command}\` ${verb} a skill from the store by its id; "${arg}" is a path.`,
|
|
146
|
+
"A skill in one of your machine's skill roots needs no install.",
|
|
147
|
+
'→ See what your machine has: skrr skills local',
|
|
148
|
+
'→ Use one on a turn: skrr agents chat <agent-id> --local-skill <name>',
|
|
149
|
+
].join('\n');
|
|
150
|
+
}
|
|
151
|
+
if (sightings.length > 0) {
|
|
152
|
+
const where = sightings
|
|
153
|
+
.map((s) => `${s.daemonId}${s.deviceName ? ` (${s.deviceName})` : ''}${s.source ? `, ${s.source}` : ''}`)
|
|
154
|
+
.join('; ');
|
|
155
|
+
return [
|
|
156
|
+
`No store skill has the id "${arg}", but a skill named "${arg}" is on your machine: ${where}.`,
|
|
157
|
+
'It needs no install.',
|
|
158
|
+
`→ Use it on a turn: skrr agents chat <agent-id> --local-skill ${arg.replace(/^\//, '')}`,
|
|
159
|
+
].join('\n');
|
|
160
|
+
}
|
|
161
|
+
return [
|
|
162
|
+
`No store skill has the id "${arg}".`,
|
|
163
|
+
'→ Find a store id: skrr skills search <term>',
|
|
164
|
+
'→ Skills on your machine are not in the store: skrr skills local',
|
|
165
|
+
].join('\n');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The first daemon release whose walker scans the skill conventions — the
|
|
169
|
+
* project's `.claude/skills`, `.agents/skills`, `.devin/skills`,
|
|
170
|
+
* `.windsurf/skills` and the global `~/.claude/skills`. It arrived in
|
|
171
|
+
* `e9dfa732cd` and first shipped in 0.8.44; an older daemon scans only
|
|
172
|
+
* `claudeSession.skillsDir`, which is empty unless someone set it by hand.
|
|
173
|
+
*/
|
|
174
|
+
exports.SKILL_CONVENTION_DISCOVERY_SINCE = '0.8.44';
|
|
175
|
+
/**
|
|
176
|
+
* Whether a daemon's reported version is older than `floor`.
|
|
177
|
+
*
|
|
178
|
+
* Unknown or unparseable versions answer false: this decides only whether to
|
|
179
|
+
* add an explanation, and claiming an unknown daemon is old would be a guess.
|
|
180
|
+
*/
|
|
181
|
+
function daemonPredates(version, floor) {
|
|
182
|
+
const parse = (value) => {
|
|
183
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(String(value ?? '').trim());
|
|
184
|
+
return match ? match.slice(1).map(Number) : null;
|
|
185
|
+
};
|
|
186
|
+
const have = parse(version);
|
|
187
|
+
const need = parse(floor);
|
|
188
|
+
if (!have || !need)
|
|
189
|
+
return false;
|
|
190
|
+
for (let i = 0; i < 3; i += 1) {
|
|
191
|
+
if (have[i] !== need[i])
|
|
192
|
+
return have[i] < need[i];
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
package/dist/lib/machines.d.ts
CHANGED
|
@@ -59,6 +59,16 @@ export interface Machine {
|
|
|
59
59
|
identityKind: MachineIdentityKind;
|
|
60
60
|
name: string;
|
|
61
61
|
status: 'online' | 'offline' | 'degraded';
|
|
62
|
+
/**
|
|
63
|
+
* One sentence naming what decided the status, or null when nothing needs
|
|
64
|
+
* explaining. Optional because the CLI ships separately from the server.
|
|
65
|
+
*
|
|
66
|
+
* `degraded` alone is unactionable, and on a laptop it is usually permanent —
|
|
67
|
+
* one offline row for a provider that was uninstalled holds the verdict
|
|
68
|
+
* forever, because supersession needs a NEWER row for the same provider and
|
|
69
|
+
* none will ever arrive.
|
|
70
|
+
*/
|
|
71
|
+
statusReason?: string | null;
|
|
62
72
|
machineUuid: string | null;
|
|
63
73
|
stableDaemonId: string | null;
|
|
64
74
|
daemonId: string | null;
|
|
@@ -135,14 +145,4 @@ export declare function listMachines(): Promise<Machine[]>;
|
|
|
135
145
|
export declare function getMachine(machineId: string): Promise<Machine | null>;
|
|
136
146
|
export declare function updateMachine(machineId: string, patch: MachinePreferencesPatch): Promise<Machine | null>;
|
|
137
147
|
export declare function disconnectMachine(machineId: string): Promise<Record<string, unknown> | null>;
|
|
138
|
-
/**
|
|
139
|
-
* Resolve a user-supplied reference to exactly one machine.
|
|
140
|
-
*
|
|
141
|
-
* A machine is addressable by several ids (`id`, `machineUuid`,
|
|
142
|
-
* `stableDaemonId`, `daemonId`) and `skrr harnesses list` prints a TRUNCATED
|
|
143
|
-
* uuid, so the id a user has to hand is often a prefix of the one the API
|
|
144
|
-
* wants. Resolving here keeps every command accepting the same references, and
|
|
145
|
-
* makes an ambiguous prefix an error rather than a silent pick of the first
|
|
146
|
-
* match.
|
|
147
|
-
*/
|
|
148
148
|
export declare function resolveMachineRef(machines: Machine[], ref: string): Machine;
|
package/dist/lib/machines.js
CHANGED
|
@@ -93,13 +93,35 @@ async function disconnectMachine(machineId) {
|
|
|
93
93
|
* makes an ambiguous prefix an error rather than a silent pick of the first
|
|
94
94
|
* match.
|
|
95
95
|
*/
|
|
96
|
+
/**
|
|
97
|
+
* The daemon ids a hosted machine's sandbox id can appear inside.
|
|
98
|
+
*
|
|
99
|
+
* `machines hosted list` prints `sbs_…`; the machine row carries
|
|
100
|
+
* `hosted-machine-sbs_…` as its daemon id. Composed rather than matched by
|
|
101
|
+
* substring so a longer id cannot be resolved by a shorter one's text.
|
|
102
|
+
*/
|
|
103
|
+
function hostedSandboxDaemonIds(ref) {
|
|
104
|
+
if (!ref.startsWith('sbs_'))
|
|
105
|
+
return [];
|
|
106
|
+
return [`hosted-machine-${ref}`, `hosted-job-machine-${ref}`];
|
|
107
|
+
}
|
|
96
108
|
function resolveMachineRef(machines, ref) {
|
|
97
109
|
const needle = ref.trim();
|
|
98
110
|
const lower = needle.toLowerCase();
|
|
99
111
|
const exact = machines.filter((m) => m.id === needle ||
|
|
100
112
|
m.machineUuid === needle ||
|
|
101
113
|
m.stableDaemonId === needle ||
|
|
102
|
-
m.daemonId === needle
|
|
114
|
+
m.daemonId === needle ||
|
|
115
|
+
// The sandbox id, which is the ONLY id `machines hosted list` prints.
|
|
116
|
+
// One hosted machine carries five identifiers and, before this, the one
|
|
117
|
+
// its own list showed you was the one `machines show` refused: the
|
|
118
|
+
// sandbox id appears here only INSIDE the daemon id, as
|
|
119
|
+
// `hosted-machine-<sbs_…>`, while `machines list --all` shows the same
|
|
120
|
+
// box as `lease:rtl_…` — a different random string entirely. Two lists,
|
|
121
|
+
// two unrelated-looking ids, no way on screen to tell they are one
|
|
122
|
+
// machine. Exact composition, not a substring search, so this cannot
|
|
123
|
+
// match a machine that merely contains the text.
|
|
124
|
+
hostedSandboxDaemonIds(needle).some((candidate) => m.daemonId === candidate));
|
|
103
125
|
if (exact.length === 1)
|
|
104
126
|
return exact[0];
|
|
105
127
|
const byName = machines.filter((m) => m.name?.toLowerCase() === lower);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The search term, from a positional or `--query`, resolved the same way by
|
|
3
|
+
* both spellings of the same search.
|
|
4
|
+
*
|
|
5
|
+
* `skills search` and `search skills` run the identical store search. Neither
|
|
6
|
+
* accepted the obvious invocation — `skrr skills search git` — so the single
|
|
7
|
+
* most natural way to call a search command failed on both, and `--query` was
|
|
8
|
+
* `required` on both, which is what made it fail rather than prompt.
|
|
9
|
+
*
|
|
10
|
+
* Shared rather than copied because the two spellings staying in step is the
|
|
11
|
+
* whole point: the reason a reader could not tell them apart was that they had
|
|
12
|
+
* drifted (different one-liners, short flags on one of them), and a second copy
|
|
13
|
+
* of this is how that starts again.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveSearchQuery(cmd: {
|
|
16
|
+
error: (message: string, options?: {
|
|
17
|
+
exit?: number;
|
|
18
|
+
}) => never;
|
|
19
|
+
}, positional: string | undefined, flag: string | undefined, command: string): string;
|