@skrr-ai/cli 0.1.107 → 0.1.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/base-command.d.ts +59 -0
- package/dist/base-command.js +118 -7
- package/dist/commands/agents/update.d.ts +14 -0
- package/dist/commands/agents/update.js +40 -1
- package/dist/commands/commitments/accounting.js +7 -2
- package/dist/commands/commitments/cost.d.ts +11 -0
- package/dist/commands/commitments/cost.js +26 -3
- package/dist/commands/commitments/create.js +2 -1
- package/dist/commands/commitments/operating-review.d.ts +15 -0
- package/dist/commands/commitments/operating-review.js +103 -50
- package/dist/commands/commitments/update.js +12 -5
- package/dist/commands/doctor.js +9 -0
- package/dist/commands/goals/list.js +6 -1
- package/dist/commands/grants/rotate.js +4 -0
- package/dist/commands/login.js +42 -14
- package/dist/commands/machines/list.js +28 -6
- package/dist/commands/projects/show.js +1 -1
- package/dist/commands/revoke-daemon.js +2 -1
- package/dist/commands/spaces/workflows/archive.d.ts +1 -0
- package/dist/commands/spaces/workflows/archive.js +1 -0
- package/dist/commands/spaces/workflows/artifact.d.ts +1 -0
- package/dist/commands/spaces/workflows/artifact.js +1 -0
- package/dist/commands/spaces/workflows/cancel-node.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel-node.js +1 -0
- package/dist/commands/spaces/workflows/cancel.d.ts +1 -0
- package/dist/commands/spaces/workflows/cancel.js +6 -2
- package/dist/commands/spaces/workflows/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/create.js +7 -1
- package/dist/commands/spaces/workflows/events.d.ts +1 -0
- package/dist/commands/spaces/workflows/events.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/create.js +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/fixtures/list.js +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +1 -0
- package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +1 -0
- package/dist/commands/spaces/workflows/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/list.js +1 -0
- package/dist/commands/spaces/workflows/node-types.d.ts +2 -0
- package/dist/commands/spaces/workflows/node-types.js +24 -6
- package/dist/commands/spaces/workflows/pause.d.ts +1 -0
- package/dist/commands/spaces/workflows/pause.js +1 -0
- package/dist/commands/spaces/workflows/plan/show.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/show.js +1 -0
- package/dist/commands/spaces/workflows/plan/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/plan/update.js +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/apply.js +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/confirm.js +1 -0
- package/dist/commands/spaces/workflows/proposals/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/create.js +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/dismiss.js +1 -0
- package/dist/commands/spaces/workflows/proposals/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/proposals/list.js +1 -0
- package/dist/commands/spaces/workflows/publish.d.ts +1 -0
- package/dist/commands/spaces/workflows/publish.js +9 -0
- package/dist/commands/spaces/workflows/reconcile.d.ts +1 -0
- package/dist/commands/spaces/workflows/reconcile.js +1 -0
- package/dist/commands/spaces/workflows/resume.d.ts +1 -0
- package/dist/commands/spaces/workflows/resume.js +1 -0
- package/dist/commands/spaces/workflows/retry.d.ts +1 -0
- package/dist/commands/spaces/workflows/retry.js +1 -0
- package/dist/commands/spaces/workflows/run.d.ts +1 -0
- package/dist/commands/spaces/workflows/run.js +14 -7
- package/dist/commands/spaces/workflows/runs.d.ts +1 -0
- package/dist/commands/spaces/workflows/runs.js +1 -0
- package/dist/commands/spaces/workflows/show.d.ts +25 -0
- package/dist/commands/spaces/workflows/show.js +42 -4
- package/dist/commands/spaces/workflows/signal.d.ts +12 -0
- package/dist/commands/spaces/workflows/signal.js +40 -2
- package/dist/commands/spaces/workflows/start.d.ts +1 -0
- package/dist/commands/spaces/workflows/start.js +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/task-execution/create.js +1 -0
- package/dist/commands/spaces/workflows/test.d.ts +1 -0
- package/dist/commands/spaces/workflows/test.js +1 -0
- package/dist/commands/spaces/workflows/triggers/create.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/create.js +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/fire.js +1 -0
- package/dist/commands/spaces/workflows/triggers/list.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/list.js +1 -0
- package/dist/commands/spaces/workflows/triggers/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/triggers/update.js +1 -0
- package/dist/commands/spaces/workflows/update.d.ts +1 -0
- package/dist/commands/spaces/workflows/update.js +4 -0
- package/dist/commands/spaces/workflows/validate.d.ts +1 -0
- package/dist/commands/spaces/workflows/validate.js +15 -0
- package/dist/commands/tasks/activity.js +6 -2
- package/dist/commands/tasks/bulk-update.d.ts +1 -0
- package/dist/commands/tasks/bulk-update.js +7 -0
- package/dist/commands/tasks/complete.js +2 -1
- package/dist/commands/tasks/create-tree.d.ts +69 -0
- package/dist/commands/tasks/create-tree.js +413 -47
- package/dist/commands/tasks/list.d.ts +2 -0
- package/dist/commands/tasks/list.js +19 -1
- package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
- package/dist/commands/tasks/rearm-recurrence.js +68 -0
- package/dist/commands/tasks/show.d.ts +1 -0
- package/dist/commands/tasks/show.js +17 -3
- package/dist/commands/tasks/timeline.js +4 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +6 -1
- package/dist/commands/triggers/create.js +8 -2
- package/dist/commands/triggers/parse-intent.d.ts +7 -0
- package/dist/commands/triggers/parse-intent.js +31 -9
- package/dist/commands/workspaces/grants.js +23 -3
- package/dist/commands/workspaces/members.d.ts +20 -0
- package/dist/commands/workspaces/members.js +64 -0
- package/dist/commands/workspaces/stewardship.js +14 -2
- package/dist/help.js +12 -0
- package/dist/hooks/command-not-found.js +27 -1
- package/dist/lib/command-miss.d.ts +52 -2
- package/dist/lib/command-miss.js +187 -18
- package/dist/lib/commitment-loop-status.d.ts +8 -0
- package/dist/lib/commitment-loop-status.js +45 -0
- package/dist/lib/commitment-product.js +13 -1
- package/dist/lib/commitments.d.ts +16 -2
- package/dist/lib/commitments.js +48 -8
- package/dist/lib/daemon-command-hint.d.ts +15 -0
- package/dist/lib/daemon-command-hint.js +21 -0
- package/dist/lib/daemon-setup.js +10 -1
- package/dist/lib/daemonBrokerRefusal.js +3 -2
- package/dist/lib/daemonHandoff.js +2 -1
- package/dist/lib/deployed-build.d.ts +43 -0
- package/dist/lib/deployed-build.js +66 -0
- package/dist/lib/grants.d.ts +27 -1
- package/dist/lib/grants.js +39 -21
- package/dist/lib/json-mode.d.ts +11 -0
- package/dist/lib/json-mode.js +19 -0
- package/dist/lib/json-output.d.ts +25 -0
- package/dist/lib/json-output.js +30 -0
- package/dist/lib/list-output.d.ts +9 -0
- package/dist/lib/list-output.js +20 -1
- package/dist/lib/local-daemon-readiness.d.ts +90 -0
- package/dist/lib/local-daemon-readiness.js +143 -0
- package/dist/lib/machines.d.ts +117 -0
- package/dist/lib/machines.js +139 -0
- package/dist/lib/managed-instruction-local.d.ts +2 -0
- package/dist/lib/managed-instruction-local.js +2 -0
- package/dist/lib/profile-machine-identity-guard.d.ts +18 -0
- package/dist/lib/profile-machine-identity-guard.js +29 -0
- package/dist/lib/request-field-flags.d.ts +20 -0
- package/dist/lib/request-field-flags.js +34 -0
- package/dist/lib/stewardship-output.d.ts +1 -1
- package/dist/lib/stewardship-output.js +26 -30
- package/dist/lib/task-batch.d.ts +61 -4
- package/dist/lib/task-batch.js +316 -80
- package/dist/lib/task-economics.d.ts +5 -0
- package/dist/lib/task-economics.js +15 -0
- package/dist/lib/task-recurrence-outcome.d.ts +6 -6
- package/dist/lib/task-recurrence-outcome.js +27 -3
- package/dist/lib/tasks.d.ts +39 -0
- package/dist/lib/tasks.js +132 -0
- package/dist/lib/triggers.d.ts +31 -1
- package/dist/lib/triggers.js +55 -1
- package/dist/lib/workflow-issues.d.ts +14 -0
- package/dist/lib/workflow-issues.js +32 -0
- package/dist/lib/workflow-run-view.d.ts +55 -0
- package/dist/lib/workflow-run-view.js +105 -0
- package/dist/lib/workspaces.d.ts +31 -4
- package/dist/lib/workspaces.js +43 -0
- package/dist/lib/write-target.d.ts +29 -0
- package/dist/lib/write-target.js +72 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/workspaceIsolationProbeWire.js +50 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.d.ts +51 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/workspaceIsolationProbeWire.js +46 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7896 -7524
- package/oclif.manifest.json +376 -52
- package/package.json +19 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.confirmLocalDaemonSignedIn = confirmLocalDaemonSignedIn;
|
|
4
|
+
exports.describeUnreadyLocalDaemon = describeUnreadyLocalDaemon;
|
|
5
|
+
exports.reportLocalDaemonAfterHandoff = reportLocalDaemonAfterHandoff;
|
|
6
|
+
const exec_runtime_binary_1 = require("./exec-runtime-binary");
|
|
7
|
+
const managed_instruction_local_1 = require("./managed-instruction-local");
|
|
8
|
+
const keychain_1 = require("./keychain");
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const runtime_start_verification_1 = require("./runtime-start-verification");
|
|
11
|
+
const daemon_command_hint_1 = require("./daemon-command-hint");
|
|
12
|
+
/** Each probe reads its inputs when called, so an injected one never touches real state. */
|
|
13
|
+
function defaultDeps() {
|
|
14
|
+
return {
|
|
15
|
+
inspect: () => (0, managed_instruction_local_1.inspectLocalInstructionDaemon)((0, keychain_1.getActiveProfile)(), (0, config_1.loadConfig)().baseURL),
|
|
16
|
+
status: async () => {
|
|
17
|
+
const profile = (0, keychain_1.getActiveProfile)();
|
|
18
|
+
const args = [
|
|
19
|
+
'status',
|
|
20
|
+
'--json',
|
|
21
|
+
...(profile !== keychain_1.DEFAULT_PROFILE ? ['--profile', profile] : []),
|
|
22
|
+
];
|
|
23
|
+
try {
|
|
24
|
+
const out = await (0, exec_runtime_binary_1.execRuntimeBinaryCapture)(args, { timeoutMs: 15_000 });
|
|
25
|
+
return { stdout: out.stdout, code: out.code };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { stdout: '', code: 1 };
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
now: () => Date.now(),
|
|
32
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function confirmLocalDaemonSignedIn(options = {}) {
|
|
36
|
+
const deps = { ...defaultDeps(), ...options.deps };
|
|
37
|
+
const waitMs = options.waitMs ?? 20_000;
|
|
38
|
+
const pollMs = options.pollMs ?? 500;
|
|
39
|
+
const stoppedGraceMs = options.stoppedGraceMs ?? 6_000;
|
|
40
|
+
const profile = (0, keychain_1.getActiveProfile)();
|
|
41
|
+
const started = deps.now();
|
|
42
|
+
let announced = false;
|
|
43
|
+
let lastDiagnosisAt = -Infinity;
|
|
44
|
+
let verdict = { state: 'not_connected', profile };
|
|
45
|
+
for (;;) {
|
|
46
|
+
const daemonId = await deps.inspect();
|
|
47
|
+
if (daemonId)
|
|
48
|
+
return { state: 'connected', daemonId };
|
|
49
|
+
if (!announced) {
|
|
50
|
+
options.log?.('Waiting for the local daemon to connect...');
|
|
51
|
+
announced = true;
|
|
52
|
+
}
|
|
53
|
+
const elapsed = deps.now() - started;
|
|
54
|
+
// Ask WHY at a measured pace — each look spawns `skrrd` — and stop waiting
|
|
55
|
+
// the moment the answer is one no amount of waiting changes.
|
|
56
|
+
if (deps.now() - lastDiagnosisAt >= 2_000) {
|
|
57
|
+
lastDiagnosisAt = deps.now();
|
|
58
|
+
const status = await deps.status();
|
|
59
|
+
const verification = (0, runtime_start_verification_1.verifyRuntimeStart)(status.stdout, status.code, ['--profile', profile]);
|
|
60
|
+
if (verification.status === 'reauth_required') {
|
|
61
|
+
return {
|
|
62
|
+
state: 'reauth_required',
|
|
63
|
+
profile: verification.profile,
|
|
64
|
+
reason: verification.reason,
|
|
65
|
+
command: verification.recoveryCommand,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (verification.status === 'not_running') {
|
|
69
|
+
verdict = {
|
|
70
|
+
state: 'not_running',
|
|
71
|
+
profile: verification.profile,
|
|
72
|
+
runtimeStatus: verification.runtimeStatus,
|
|
73
|
+
};
|
|
74
|
+
// A service that was just started reads `stopped` for a moment between
|
|
75
|
+
// launchd accepting the request and the process existing; only believe
|
|
76
|
+
// it once it has had that long.
|
|
77
|
+
if (elapsed >= stoppedGraceMs)
|
|
78
|
+
return verdict;
|
|
79
|
+
}
|
|
80
|
+
else if (verification.status === 'unreadable_status') {
|
|
81
|
+
verdict = { state: 'unreadable', profile: verification.profile };
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
verdict = { state: 'not_connected', profile };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (elapsed >= waitMs)
|
|
88
|
+
return verdict;
|
|
89
|
+
await deps.sleep(Math.min(pollMs, Math.max(0, waitMs - elapsed)));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* What `skrr login` prints about a daemon that was handed a credential and did
|
|
94
|
+
* not come up with it: what happened, why, and the one command. Pure, so every
|
|
95
|
+
* state can be read without a daemon.
|
|
96
|
+
*/
|
|
97
|
+
function describeUnreadyLocalDaemon(readiness, commands) {
|
|
98
|
+
switch (readiness.state) {
|
|
99
|
+
case 'reauth_required':
|
|
100
|
+
return [
|
|
101
|
+
`The local daemon received its sign-in but cannot use it: ${readiness.reason === 'TOKEN_MISSING'
|
|
102
|
+
? 'it started without a credential'
|
|
103
|
+
: `the server refused it (${readiness.reason ?? 'UNKNOWN'})`}.`,
|
|
104
|
+
` Sign it in with: ${readiness.command}`,
|
|
105
|
+
];
|
|
106
|
+
case 'not_running':
|
|
107
|
+
return [
|
|
108
|
+
`The local daemon received its sign-in but is not running (service status: ${readiness.runtimeStatus ?? 'unknown'}).`,
|
|
109
|
+
` See why with \`${commands.logs}\`, then start it with \`${commands.start}\`.`,
|
|
110
|
+
];
|
|
111
|
+
case 'not_connected':
|
|
112
|
+
return [
|
|
113
|
+
'The local daemon received its sign-in and is running, but has not connected yet.',
|
|
114
|
+
` Check it with \`${commands.status}\`.`,
|
|
115
|
+
];
|
|
116
|
+
case 'unreadable':
|
|
117
|
+
return [
|
|
118
|
+
'The local daemon received its sign-in, but its status could not be read to confirm it.',
|
|
119
|
+
` Check it with \`${commands.status}\`.`,
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The end of `skrr login` for a daemon that was handed a credential: wait for it
|
|
125
|
+
* to connect, then say ONE true thing. "Signed in too" only for a daemon that
|
|
126
|
+
* verified connected as this account; otherwise what happened, why, and the one
|
|
127
|
+
* command — and never both, which is what the login used to do.
|
|
128
|
+
*/
|
|
129
|
+
async function reportLocalDaemonAfterHandoff(options) {
|
|
130
|
+
const readiness = await (options.confirm ?? confirmLocalDaemonSignedIn)({ log: options.log });
|
|
131
|
+
if (readiness.state === 'connected') {
|
|
132
|
+
options.log(`The local daemon was signed in too — ${options.deliveredDetail}.`);
|
|
133
|
+
return { daemonId: readiness.daemonId, notReady: false };
|
|
134
|
+
}
|
|
135
|
+
for (const line of describeUnreadyLocalDaemon(readiness, {
|
|
136
|
+
status: (0, daemon_command_hint_1.daemonCommandHint)('status', { bin: options.bin }),
|
|
137
|
+
logs: (0, daemon_command_hint_1.daemonCommandHint)('logs', { bin: options.bin }),
|
|
138
|
+
start: (0, daemon_command_hint_1.daemonCommandHint)('start', { bin: options.bin }),
|
|
139
|
+
})) {
|
|
140
|
+
options.log(line);
|
|
141
|
+
}
|
|
142
|
+
return { notReady: true };
|
|
143
|
+
}
|
package/dist/lib/machines.d.ts
CHANGED
|
@@ -53,6 +53,35 @@ export interface MachineHarnessRow {
|
|
|
53
53
|
* them.
|
|
54
54
|
*/
|
|
55
55
|
superseded?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Recorded `online`, but no heartbeat for longer than the server's stale
|
|
58
|
+
* threshold (`MachineContact.staleAfterMs`). Present only when true. The server
|
|
59
|
+
* decides this once; a row's own `status` stays what was recorded
|
|
60
|
+
* (OSK-16442).
|
|
61
|
+
*/
|
|
62
|
+
stale?: boolean;
|
|
63
|
+
/** How long that row has been silent, in ms. Present with `stale`. */
|
|
64
|
+
staleForMs?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* What a machine's heartbeats say, apart from what its rows are recorded as
|
|
68
|
+
* (OSK-16442). A daemon that was stopped or frozen stays recorded `online` for a
|
|
69
|
+
* minute or more before the server gives up on it, so `status` alone said
|
|
70
|
+
* `online` for a process that had not been heard from in a minute.
|
|
71
|
+
*
|
|
72
|
+
* - `live`: a row is online and heard from recently.
|
|
73
|
+
* - `stale`: rows are recorded online, but none has been heard from for
|
|
74
|
+
* `staleAfterMs`. The machine's `status` is `degraded` meanwhile.
|
|
75
|
+
* - `offline`: nothing is recorded online.
|
|
76
|
+
*
|
|
77
|
+
* The thresholds are the server's, sent so this CLI never restates them. Absent
|
|
78
|
+
* on a server older than this field and on a lease-backed machine.
|
|
79
|
+
*/
|
|
80
|
+
export interface MachineContact {
|
|
81
|
+
state: 'live' | 'stale' | 'offline';
|
|
82
|
+
staleForMs: number | null;
|
|
83
|
+
staleAfterMs: number;
|
|
84
|
+
offlineAfterMs: number;
|
|
56
85
|
}
|
|
57
86
|
export interface Machine {
|
|
58
87
|
id: string;
|
|
@@ -69,6 +98,7 @@ export interface Machine {
|
|
|
69
98
|
* none will ever arrive.
|
|
70
99
|
*/
|
|
71
100
|
statusReason?: string | null;
|
|
101
|
+
contact?: MachineContact | null;
|
|
72
102
|
machineUuid: string | null;
|
|
73
103
|
stableDaemonId: string | null;
|
|
74
104
|
daemonId: string | null;
|
|
@@ -120,6 +150,93 @@ export declare function machineStateLabel(machine: Machine): string;
|
|
|
120
150
|
export declare function hiddenMachinesSummary(hidden: readonly Machine[]): string;
|
|
121
151
|
/** Is this a machine a user could still use, or bring back? */
|
|
122
152
|
export declare function isReachableMachine(machine: Machine): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* A computer somebody installed a daemon on, as opposed to a hosted sandbox.
|
|
155
|
+
*
|
|
156
|
+
* A daemon launched on a person's own computer registers as
|
|
157
|
+
* `daemon_<hash>_<source>[_<profile>]` (`parseDaemonId`); a hosted or cloud
|
|
158
|
+
* machine's daemon carries an id minted by the control plane
|
|
159
|
+
* (`hosted-machine-…`, `cloud-coding-…`) that names no launch source. A lease
|
|
160
|
+
* decides a managed machine's own state, so it is never one of these.
|
|
161
|
+
*/
|
|
162
|
+
export declare function isOwnComputer(machine: Machine): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* What the default `machines list` keeps: everything reachable, and every one of
|
|
165
|
+
* the person's own computers while it is offline (OSK-16442).
|
|
166
|
+
*
|
|
167
|
+
* The default hides offline machines because hosted sandboxes accumulate by the
|
|
168
|
+
* hundreds and buried the one that was usable (OSK-7639). Applied to a computer
|
|
169
|
+
* it did something else: the moment its last daemon stopped, the whole row left
|
|
170
|
+
* the list, in text and in `--json`, so "my computer is offline" and "my computer
|
|
171
|
+
* is gone" read the same. A computer is a handful of rows however long it is
|
|
172
|
+
* kept, and `offline` with its last-seen time is the answer a person asks the
|
|
173
|
+
* list for, so it stays; only what can no longer be used is left behind `--all`.
|
|
174
|
+
*/
|
|
175
|
+
export declare function isListedByDefault(machine: Machine): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* One daemon registration on a machine, as its own record (OSK-16440).
|
|
178
|
+
*
|
|
179
|
+
* A machine row aggregates every harness registration ever made under one
|
|
180
|
+
* hardware identity. The daemon that made each is only a string on a harness
|
|
181
|
+
* row, and the profile it ran as is a suffix of that string — so a script that
|
|
182
|
+
* wanted "the daemon that just registered" had to take the first unfamiliar
|
|
183
|
+
* `daemonId` out of a flat list that, depending on whether anything on the
|
|
184
|
+
* computer happened to be online, held 4 rows or 98 of them.
|
|
185
|
+
*/
|
|
186
|
+
export interface MachineDaemonSummary {
|
|
187
|
+
daemonId: string;
|
|
188
|
+
/** The profile the daemon ran as, read from its id; null for an id that names none (a hosted box). */
|
|
189
|
+
profile: string | null;
|
|
190
|
+
/** How it was launched (`cli`, `launchd`, …); null when the id does not say. */
|
|
191
|
+
source: string | null;
|
|
192
|
+
/**
|
|
193
|
+
* `online` when any registration is online and heard from recently, `stale`
|
|
194
|
+
* when some are recorded online but none has been heard from lately, `offline`
|
|
195
|
+
* otherwise.
|
|
196
|
+
*/
|
|
197
|
+
status: 'online' | 'stale' | 'offline';
|
|
198
|
+
lastSeenAt: string | null;
|
|
199
|
+
harnessCount: number;
|
|
200
|
+
/** Of those, how many a newer registration of the same provider replaced. */
|
|
201
|
+
supersededCount: number;
|
|
202
|
+
/** The profile this CLI is running as. */
|
|
203
|
+
activeProfile: boolean;
|
|
204
|
+
}
|
|
205
|
+
export declare function parseDaemonId(daemonId: string): {
|
|
206
|
+
source: string;
|
|
207
|
+
profile: string;
|
|
208
|
+
} | null;
|
|
209
|
+
export declare function summarizeDaemons(harnesses: readonly MachineHarnessRow[], activeProfile: string): MachineDaemonSummary[];
|
|
210
|
+
/** Whether the machine's identity is THIS computer's (the default profile's, or a profile qualified from it). */
|
|
211
|
+
export declare function isThisComputer(machine: Machine, hardwareMachineUuid: string): boolean;
|
|
212
|
+
export interface MachineListView extends Machine {
|
|
213
|
+
/** This machine is the computer the CLI is running on. */
|
|
214
|
+
thisComputer: boolean;
|
|
215
|
+
/** One record per daemon registration; see {@link MachineDaemonSummary}. */
|
|
216
|
+
daemons: MachineDaemonSummary[];
|
|
217
|
+
/**
|
|
218
|
+
* Harness rows left out of `harnesses` because a newer registration replaced
|
|
219
|
+
* them (default view only). `supersededHarnessCount` still reports the
|
|
220
|
+
* server's own total, so nothing is silently subtracted.
|
|
221
|
+
*/
|
|
222
|
+
hiddenHarnessCount?: number;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* What `machines list` shows of one machine (OSK-16440).
|
|
226
|
+
*
|
|
227
|
+
* The default list is "what is reachable now", and a machine's own history is
|
|
228
|
+
* not part of that. A machine row carries every harness registration it ever
|
|
229
|
+
* had; one of them being online made the whole 98-row history appear in a list
|
|
230
|
+
* that, a minute earlier, had held four rows — the same command, a different
|
|
231
|
+
* answer, for no reason a reader could see. So the default view keeps the
|
|
232
|
+
* registrations that still stand and says how many it left out; `--all` keeps
|
|
233
|
+
* everything.
|
|
234
|
+
*/
|
|
235
|
+
export declare function machineListView(machine: Machine, options: {
|
|
236
|
+
all: boolean;
|
|
237
|
+
hardwareMachineUuid: string;
|
|
238
|
+
activeProfile: string;
|
|
239
|
+
}): MachineListView;
|
|
123
240
|
/** The user-editable slice. Identities, capabilities and lifecycle are not. */
|
|
124
241
|
export interface MachinePreferencesPatch {
|
|
125
242
|
name?: string;
|
package/dist/lib/machines.js
CHANGED
|
@@ -3,6 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.machineStateLabel = machineStateLabel;
|
|
4
4
|
exports.hiddenMachinesSummary = hiddenMachinesSummary;
|
|
5
5
|
exports.isReachableMachine = isReachableMachine;
|
|
6
|
+
exports.isOwnComputer = isOwnComputer;
|
|
7
|
+
exports.isListedByDefault = isListedByDefault;
|
|
8
|
+
exports.parseDaemonId = parseDaemonId;
|
|
9
|
+
exports.summarizeDaemons = summarizeDaemons;
|
|
10
|
+
exports.isThisComputer = isThisComputer;
|
|
11
|
+
exports.machineListView = machineListView;
|
|
6
12
|
exports.liveHarnessCount = liveHarnessCount;
|
|
7
13
|
exports.listMachines = listMachines;
|
|
8
14
|
exports.getMachine = getMachine;
|
|
@@ -25,8 +31,20 @@ const api_fetch_1 = require("./api-fetch");
|
|
|
25
31
|
* could come back says so, and a destroyed one cannot.
|
|
26
32
|
*/
|
|
27
33
|
function machineStateLabel(machine) {
|
|
34
|
+
// `degraded` is the machine's status while a daemon is silent, and it reads the
|
|
35
|
+
// same as a sign-in problem or one provider being down. The server says which
|
|
36
|
+
// it is; the word a person reads is that one (OSK-16442).
|
|
37
|
+
if (machine.status === 'degraded' && machine.contact?.state === 'stale')
|
|
38
|
+
return 'stale';
|
|
28
39
|
if (machine.status !== 'offline')
|
|
29
40
|
return machine.status;
|
|
41
|
+
// A computer somebody installed a daemon on has no lease to resume and nothing
|
|
42
|
+
// to terminate, which is also exactly what a destroyed sandbox looks like, so
|
|
43
|
+
// the check below read every switched-off computer as `terminated`: the one
|
|
44
|
+
// word for "this can never come back", beside a machine that comes back the
|
|
45
|
+
// moment its daemon starts (OSK-16442).
|
|
46
|
+
if (isOwnComputer(machine))
|
|
47
|
+
return 'offline';
|
|
30
48
|
const controls = machine.controls;
|
|
31
49
|
if (controls && controls.canResume === false && controls.canTerminate === false) {
|
|
32
50
|
return 'terminated';
|
|
@@ -65,6 +83,127 @@ function hiddenMachinesSummary(hidden) {
|
|
|
65
83
|
function isReachableMachine(machine) {
|
|
66
84
|
return machineStateLabel(machine) !== 'terminated' && machine.status !== 'offline';
|
|
67
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* A computer somebody installed a daemon on, as opposed to a hosted sandbox.
|
|
88
|
+
*
|
|
89
|
+
* A daemon launched on a person's own computer registers as
|
|
90
|
+
* `daemon_<hash>_<source>[_<profile>]` (`parseDaemonId`); a hosted or cloud
|
|
91
|
+
* machine's daemon carries an id minted by the control plane
|
|
92
|
+
* (`hosted-machine-…`, `cloud-coding-…`) that names no launch source. A lease
|
|
93
|
+
* decides a managed machine's own state, so it is never one of these.
|
|
94
|
+
*/
|
|
95
|
+
function isOwnComputer(machine) {
|
|
96
|
+
if (machine.lease)
|
|
97
|
+
return false;
|
|
98
|
+
return (machine.harnesses ?? []).some((row) => typeof row.daemonId === 'string' && parseDaemonId(row.daemonId) !== null);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What the default `machines list` keeps: everything reachable, and every one of
|
|
102
|
+
* the person's own computers while it is offline (OSK-16442).
|
|
103
|
+
*
|
|
104
|
+
* The default hides offline machines because hosted sandboxes accumulate by the
|
|
105
|
+
* hundreds and buried the one that was usable (OSK-7639). Applied to a computer
|
|
106
|
+
* it did something else: the moment its last daemon stopped, the whole row left
|
|
107
|
+
* the list, in text and in `--json`, so "my computer is offline" and "my computer
|
|
108
|
+
* is gone" read the same. A computer is a handful of rows however long it is
|
|
109
|
+
* kept, and `offline` with its last-seen time is the answer a person asks the
|
|
110
|
+
* list for, so it stays; only what can no longer be used is left behind `--all`.
|
|
111
|
+
*/
|
|
112
|
+
function isListedByDefault(machine) {
|
|
113
|
+
if (isReachableMachine(machine))
|
|
114
|
+
return true;
|
|
115
|
+
return isOwnComputer(machine) && machineStateLabel(machine) === 'offline';
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `daemon_<hash>_<source>_<profile>` — the closed set of launch sources is what
|
|
119
|
+
* makes the profile recoverable, since a profile name may itself contain an
|
|
120
|
+
* underscore and a source never does (`HarnessService`'s runtime key parses the
|
|
121
|
+
* same id the same way). An id that predates profiles ends at the source.
|
|
122
|
+
*/
|
|
123
|
+
const DAEMON_ID_PATTERN = /^daemon_[0-9a-f]+_(cli|launchd|systemd|schtasks|electron)(?:_(.+))?$/;
|
|
124
|
+
function parseDaemonId(daemonId) {
|
|
125
|
+
const match = DAEMON_ID_PATTERN.exec(daemonId);
|
|
126
|
+
return match ? { source: match[1], profile: match[2] ?? 'default' } : null;
|
|
127
|
+
}
|
|
128
|
+
function newerStamp(a, b) {
|
|
129
|
+
if (!a)
|
|
130
|
+
return b;
|
|
131
|
+
if (!b)
|
|
132
|
+
return a;
|
|
133
|
+
return new Date(b).getTime() > new Date(a).getTime() ? b : a;
|
|
134
|
+
}
|
|
135
|
+
function summarizeDaemons(harnesses, activeProfile) {
|
|
136
|
+
const byDaemon = new Map();
|
|
137
|
+
for (const row of harnesses) {
|
|
138
|
+
if (!row.daemonId)
|
|
139
|
+
continue;
|
|
140
|
+
const parsed = parseDaemonId(row.daemonId);
|
|
141
|
+
const existing = byDaemon.get(row.daemonId);
|
|
142
|
+
const entry = existing ?? {
|
|
143
|
+
daemonId: row.daemonId,
|
|
144
|
+
profile: parsed?.profile ?? null,
|
|
145
|
+
source: parsed?.source ?? null,
|
|
146
|
+
status: 'offline',
|
|
147
|
+
lastSeenAt: null,
|
|
148
|
+
harnessCount: 0,
|
|
149
|
+
supersededCount: 0,
|
|
150
|
+
activeProfile: parsed !== null && parsed.profile === activeProfile,
|
|
151
|
+
};
|
|
152
|
+
entry.harnessCount += 1;
|
|
153
|
+
if (row.superseded === true)
|
|
154
|
+
entry.supersededCount += 1;
|
|
155
|
+
if (row.status === 'online') {
|
|
156
|
+
if (row.stale !== true)
|
|
157
|
+
entry.status = 'online';
|
|
158
|
+
else if (entry.status === 'offline')
|
|
159
|
+
entry.status = 'stale';
|
|
160
|
+
}
|
|
161
|
+
entry.lastSeenAt = newerStamp(entry.lastSeenAt, row.lastSeenAt);
|
|
162
|
+
byDaemon.set(row.daemonId, entry);
|
|
163
|
+
}
|
|
164
|
+
// Connected first, then silent, then gone, each by most recently seen: the one a
|
|
165
|
+
// person is looking for is on top.
|
|
166
|
+
const rank = { online: 0, stale: 1, offline: 2 };
|
|
167
|
+
return [...byDaemon.values()].sort((a, b) => {
|
|
168
|
+
if (a.status !== b.status)
|
|
169
|
+
return rank[a.status] - rank[b.status];
|
|
170
|
+
return new Date(b.lastSeenAt ?? 0).getTime() - new Date(a.lastSeenAt ?? 0).getTime();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** Whether the machine's identity is THIS computer's (the default profile's, or a profile qualified from it). */
|
|
174
|
+
function isThisComputer(machine, hardwareMachineUuid) {
|
|
175
|
+
const uuid = machine.machineUuid;
|
|
176
|
+
if (!uuid || !hardwareMachineUuid)
|
|
177
|
+
return false;
|
|
178
|
+
return uuid === hardwareMachineUuid || uuid.startsWith(`${hardwareMachineUuid}:p-`);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* What `machines list` shows of one machine (OSK-16440).
|
|
182
|
+
*
|
|
183
|
+
* The default list is "what is reachable now", and a machine's own history is
|
|
184
|
+
* not part of that. A machine row carries every harness registration it ever
|
|
185
|
+
* had; one of them being online made the whole 98-row history appear in a list
|
|
186
|
+
* that, a minute earlier, had held four rows — the same command, a different
|
|
187
|
+
* answer, for no reason a reader could see. So the default view keeps the
|
|
188
|
+
* registrations that still stand and says how many it left out; `--all` keeps
|
|
189
|
+
* everything.
|
|
190
|
+
*/
|
|
191
|
+
function machineListView(machine, options) {
|
|
192
|
+
const rows = machine.harnesses ?? [];
|
|
193
|
+
const daemons = summarizeDaemons(rows, options.activeProfile);
|
|
194
|
+
const visibleRows = options.all ? rows : rows.filter((row) => row.superseded !== true);
|
|
195
|
+
const visibleDaemons = options.all
|
|
196
|
+
? daemons
|
|
197
|
+
: daemons.filter((daemon) => daemon.harnessCount > daemon.supersededCount);
|
|
198
|
+
const hidden = rows.length - visibleRows.length;
|
|
199
|
+
return {
|
|
200
|
+
...machine,
|
|
201
|
+
thisComputer: isThisComputer(machine, options.hardwareMachineUuid),
|
|
202
|
+
daemons: visibleDaemons,
|
|
203
|
+
harnesses: visibleRows,
|
|
204
|
+
...(hidden > 0 ? { hiddenHarnessCount: hidden } : {}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
68
207
|
/**
|
|
69
208
|
* How many of a machine's harnesses still represent a capability it has.
|
|
70
209
|
*
|
|
@@ -10,3 +10,5 @@ export declare function resolveLocalInstructionDaemon(options?: {
|
|
|
10
10
|
waitMs?: number;
|
|
11
11
|
log?: (message: string) => void;
|
|
12
12
|
}): Promise<string | null>;
|
|
13
|
+
/** One look at this profile's loopback identity endpoint: the daemon id when it is connected as this account. */
|
|
14
|
+
export declare function inspectLocalInstructionDaemon(profile: string, serverUrl: string): Promise<string | null>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveLocalInstructionDaemon = resolveLocalInstructionDaemon;
|
|
4
|
+
exports.inspectLocalInstructionDaemon = inspectLocalInstructionDaemon;
|
|
4
5
|
const loopback_http_1 = require("@skrr-ai/auth-core/loopback-http");
|
|
5
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
7
|
const config_1 = require("./config");
|
|
@@ -25,6 +26,7 @@ async function resolveLocalInstructionDaemon(options = {}) {
|
|
|
25
26
|
await new Promise((resolve) => setTimeout(resolve, Math.min(300, deadline - Date.now())));
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
/** One look at this profile's loopback identity endpoint: the daemon id when it is connected as this account. */
|
|
28
30
|
async function inspectLocalInstructionDaemon(profile, serverUrl) {
|
|
29
31
|
for (const candidate of (0, daemonBroker_1.resolveExactProfileBootstrapPaths)(profile)) {
|
|
30
32
|
const bootstrap = (0, daemonBroker_1.readBootstrap)(candidate);
|
|
@@ -1,2 +1,20 @@
|
|
|
1
1
|
/** An old profile may still share the default machine row and its device pin. */
|
|
2
2
|
export declare function legacyProfileSharesDefaultMachine(): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Why a sign-in on this profile stopped, and what to do instead (OSK-16436).
|
|
5
|
+
*
|
|
6
|
+
* This said the profile "was created before profiles had separate machine
|
|
7
|
+
* identities". For someone whose `skrr login --profile work` ran minutes ago,
|
|
8
|
+
* under a CLI that then updated itself, that is false in time and unhelpful in
|
|
9
|
+
* substance: the profile is new to them and old to the program. What is true is
|
|
10
|
+
* a fact about the VERSION that set it up — the older one gave every profile the
|
|
11
|
+
* computer's machine identity, and this profile still holds a credential bound
|
|
12
|
+
* to it — and a consequence: signing in again re-pins the default profile's
|
|
13
|
+
* device key and signs its daemon out.
|
|
14
|
+
*
|
|
15
|
+
* The one command is the CLI's own (`skrr login --profile NAME`), which signs
|
|
16
|
+
* the background service in as well; `skrrd --profile NAME login` is the
|
|
17
|
+
* daemon-only spelling and is named second. The suggested name is concrete so
|
|
18
|
+
* the line can be pasted.
|
|
19
|
+
*/
|
|
20
|
+
export declare function describeLegacySharedProfile(profile: string): string[];
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.legacyProfileSharesDefaultMachine = legacyProfileSharesDefaultMachine;
|
|
4
|
+
exports.describeLegacySharedProfile = describeLegacySharedProfile;
|
|
4
5
|
const machine_uuid_1 = require("@skrr-ai/auth-core/machine-uuid");
|
|
6
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
7
|
const keychain_1 = require("./keychain");
|
|
6
8
|
/** An old profile may still share the default machine row and its device pin. */
|
|
7
9
|
function legacyProfileSharesDefaultMachine() {
|
|
@@ -11,3 +13,30 @@ function legacyProfileSharesDefaultMachine() {
|
|
|
11
13
|
!(0, machine_uuid_1.hostedRuntimeIdOverride)('OVERSKY_MACHINE_UUID') &&
|
|
12
14
|
(0, machine_uuid_1.getMachineUuid)() === (0, machine_uuid_1.getHardwareMachineUuid)());
|
|
13
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Why a sign-in on this profile stopped, and what to do instead (OSK-16436).
|
|
18
|
+
*
|
|
19
|
+
* This said the profile "was created before profiles had separate machine
|
|
20
|
+
* identities". For someone whose `skrr login --profile work` ran minutes ago,
|
|
21
|
+
* under a CLI that then updated itself, that is false in time and unhelpful in
|
|
22
|
+
* substance: the profile is new to them and old to the program. What is true is
|
|
23
|
+
* a fact about the VERSION that set it up — the older one gave every profile the
|
|
24
|
+
* computer's machine identity, and this profile still holds a credential bound
|
|
25
|
+
* to it — and a consequence: signing in again re-pins the default profile's
|
|
26
|
+
* device key and signs its daemon out.
|
|
27
|
+
*
|
|
28
|
+
* The one command is the CLI's own (`skrr login --profile NAME`), which signs
|
|
29
|
+
* the background service in as well; `skrrd --profile NAME login` is the
|
|
30
|
+
* daemon-only spelling and is named second. The suggested name is concrete so
|
|
31
|
+
* the line can be pasted.
|
|
32
|
+
*/
|
|
33
|
+
function describeLegacySharedProfile(profile) {
|
|
34
|
+
const next = `${profile}-2`;
|
|
35
|
+
return [
|
|
36
|
+
`Profile '${profile}' was set up by an older ${data_provider_1.BRAND.displayName} version that gave every profile on this ` +
|
|
37
|
+
'computer the same machine identity, and it still has it. Signing in here would replace the default ' +
|
|
38
|
+
"profile's device-key pin and sign the default daemon out. Nothing was changed.",
|
|
39
|
+
`Use a new profile name instead: skrr login --profile ${next}`,
|
|
40
|
+
`(For the background service alone: skrrd --profile ${next} login)`,
|
|
41
|
+
];
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a server refusal back in the words the person typed.
|
|
3
|
+
*
|
|
4
|
+
* The API validates the request BODY, so its messages name body paths:
|
|
5
|
+
* `execution.workflowSignal is required for mode "workflow_signal"`. A person who built that body
|
|
6
|
+
* from flags never typed `execution.workflowSignal`, and there is no flag of that name, so the
|
|
7
|
+
* message pointed at nothing they could change (OSK-15313). A command that builds its body from
|
|
8
|
+
* flags declares which flag writes which path, and `handleApiError` rewrites any path it finds in
|
|
9
|
+
* the server's message to that flag. The server keeps speaking its own contract; the CLI, which
|
|
10
|
+
* is the only party that knows how the body was built, does the translation.
|
|
11
|
+
*
|
|
12
|
+
* A command that sent a body the person wrote themselves (`--from-json`) declares nothing: then
|
|
13
|
+
* the path IS what they typed.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Replace every declared body path in `message` with its flag. Longest path first, so
|
|
17
|
+
* `execution.workflowSignal.eventKey` is not half-rewritten by `execution.workflowSignal`; a
|
|
18
|
+
* path is only replaced as a whole token (not when followed by more of a longer path).
|
|
19
|
+
*/
|
|
20
|
+
export declare function translateRequestFieldPaths(message: string | undefined, fieldFlags: Readonly<Record<string, string>> | undefined): string | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Read a server refusal back in the words the person typed.
|
|
4
|
+
*
|
|
5
|
+
* The API validates the request BODY, so its messages name body paths:
|
|
6
|
+
* `execution.workflowSignal is required for mode "workflow_signal"`. A person who built that body
|
|
7
|
+
* from flags never typed `execution.workflowSignal`, and there is no flag of that name, so the
|
|
8
|
+
* message pointed at nothing they could change (OSK-15313). A command that builds its body from
|
|
9
|
+
* flags declares which flag writes which path, and `handleApiError` rewrites any path it finds in
|
|
10
|
+
* the server's message to that flag. The server keeps speaking its own contract; the CLI, which
|
|
11
|
+
* is the only party that knows how the body was built, does the translation.
|
|
12
|
+
*
|
|
13
|
+
* A command that sent a body the person wrote themselves (`--from-json`) declares nothing: then
|
|
14
|
+
* the path IS what they typed.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.translateRequestFieldPaths = translateRequestFieldPaths;
|
|
18
|
+
function escapeRegExp(value) {
|
|
19
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Replace every declared body path in `message` with its flag. Longest path first, so
|
|
23
|
+
* `execution.workflowSignal.eventKey` is not half-rewritten by `execution.workflowSignal`; a
|
|
24
|
+
* path is only replaced as a whole token (not when followed by more of a longer path).
|
|
25
|
+
*/
|
|
26
|
+
function translateRequestFieldPaths(message, fieldFlags) {
|
|
27
|
+
if (!message || !fieldFlags)
|
|
28
|
+
return message;
|
|
29
|
+
const paths = Object.keys(fieldFlags).sort((a, b) => b.length - a.length);
|
|
30
|
+
if (paths.length === 0)
|
|
31
|
+
return message;
|
|
32
|
+
const pattern = new RegExp(`(?<![\\w.])(${paths.map(escapeRegExp).join('|')})(?![\\w.]*\\w)`, 'g');
|
|
33
|
+
return message.replace(pattern, (path) => fieldFlags[path] ?? path);
|
|
34
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { WorkspaceGrantReport, WorkspaceStewardshipReport } from './workspaces';
|
|
2
2
|
export declare function printStewardshipCoverage(report: WorkspaceGrantReport, log: (line: string) => void): void;
|
|
3
|
-
export declare function printWorkspaceGrants(report: WorkspaceGrantReport, log: (line: string) => void): void;
|
|
3
|
+
export declare function printWorkspaceGrants(report: WorkspaceGrantReport, log: (line: string) => void, members?: ReadonlyMap<string, string>): void;
|
|
4
4
|
export declare function printWorkspaceStewardship(report: WorkspaceStewardshipReport, log: (line: string) => void): void;
|