@skrr-ai/cli 0.1.87 → 0.1.89
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/commands/agents/chat.js +27 -1
- package/dist/commands/computer/index.d.ts +19 -0
- package/dist/commands/computer/index.js +37 -0
- package/dist/commands/labels/create.d.ts +1 -0
- package/dist/commands/labels/create.js +2 -0
- package/dist/commands/labels/restore.d.ts +1 -0
- package/dist/commands/labels/restore.js +6 -3
- package/dist/commands/labels/update.d.ts +1 -0
- package/dist/commands/labels/update.js +2 -0
- package/dist/commands/machines/computer-adoption.d.ts +16 -0
- package/dist/commands/machines/computer-adoption.js +34 -0
- package/dist/commands/machines/dedicated/agent-browser.d.ts +25 -0
- package/dist/commands/machines/dedicated/agent-browser.js +65 -0
- package/dist/commands/machines/dedicated/archive.d.ts +26 -0
- package/dist/commands/machines/dedicated/archive.js +113 -0
- package/dist/commands/machines/dedicated/audit.d.ts +25 -0
- package/dist/commands/machines/dedicated/audit.js +113 -0
- package/dist/commands/machines/dedicated/computer.d.ts +23 -0
- package/dist/commands/machines/dedicated/computer.js +64 -0
- package/dist/commands/machines/dedicated/cp.d.ts +2 -0
- package/dist/commands/machines/dedicated/cp.js +74 -2
- package/dist/commands/machines/dedicated/git-status.d.ts +15 -0
- package/dist/commands/machines/dedicated/git-status.js +56 -0
- package/dist/commands/machines/dedicated/index.js +15 -1
- package/dist/commands/machines/dedicated/ls.d.ts +27 -0
- package/dist/commands/machines/dedicated/ls.js +100 -0
- package/dist/commands/machines/dedicated/mkdir.d.ts +18 -0
- package/dist/commands/machines/dedicated/mkdir.js +57 -0
- package/dist/commands/machines/dedicated/mv.d.ts +24 -0
- package/dist/commands/machines/dedicated/mv.js +83 -0
- package/dist/commands/machines/dedicated/rm.d.ts +26 -0
- package/dist/commands/machines/dedicated/rm.js +100 -0
- package/dist/commands/machines/dedicated/search.d.ts +24 -0
- package/dist/commands/machines/dedicated/search.js +78 -0
- package/dist/commands/machines/dedicated/show.js +17 -2
- package/dist/commands/machines/dedicated/stat.d.ts +22 -0
- package/dist/commands/machines/dedicated/stat.js +92 -0
- package/dist/commands/machines/hosted/index.js +1 -1
- package/dist/commands/machines/hosted/list.js +1 -1
- package/dist/commands/machines/hosted/start.js +1 -1
- package/dist/commands/machines/services/declare.d.ts +24 -0
- package/dist/commands/machines/services/declare.js +73 -0
- package/dist/commands/machines/services/index.d.ts +15 -0
- package/dist/commands/machines/services/index.js +31 -0
- package/dist/commands/machines/services/ls.d.ts +17 -0
- package/dist/commands/machines/services/ls.js +60 -0
- package/dist/commands/machines/services/withdraw.d.ts +19 -0
- package/dist/commands/machines/services/withdraw.js +47 -0
- package/dist/commands/machines/share.d.ts +29 -0
- package/dist/commands/machines/share.js +100 -0
- package/dist/commands/machines/shared.d.ts +19 -0
- package/dist/commands/machines/shared.js +62 -0
- package/dist/commands/machines/shares.d.ts +16 -0
- package/dist/commands/machines/shares.js +69 -0
- package/dist/commands/machines/unshare.d.ts +19 -0
- package/dist/commands/machines/unshare.js +61 -0
- package/dist/commands/tasks/labels/create.d.ts +1 -0
- package/dist/commands/tasks/labels/create.js +3 -0
- package/dist/commands/views/create.d.ts +1 -0
- package/dist/commands/views/create.js +5 -0
- package/dist/lib/agent-home-workspace.d.ts +7 -0
- package/dist/lib/agent-home-workspace.js +147 -0
- package/dist/lib/computer-adoption.d.ts +60 -0
- package/dist/lib/computer-adoption.js +72 -0
- package/dist/lib/computer-consent.d.ts +55 -0
- package/dist/lib/computer-consent.js +154 -0
- package/dist/lib/computer-files.d.ts +239 -0
- package/dist/lib/computer-files.js +707 -0
- package/dist/lib/dedicated-copy.d.ts +45 -9
- package/dist/lib/dedicated-copy.js +141 -44
- package/dist/lib/dedicated-machines.d.ts +80 -0
- package/dist/lib/dedicated-machines.js +137 -6
- package/dist/lib/exec-runtime-binary.d.ts +3 -1
- package/dist/lib/exec-runtime-binary.js +16 -2
- package/dist/lib/label-scope.d.ts +12 -0
- package/dist/lib/label-scope.js +15 -1
- package/dist/lib/machine-audit.d.ts +34 -0
- package/dist/lib/machine-audit.js +50 -0
- package/dist/lib/machine-grants.d.ts +70 -0
- package/dist/lib/machine-grants.js +47 -0
- package/dist/lib/machine-services.d.ts +64 -0
- package/dist/lib/machine-services.js +60 -0
- package/dist/lib/task-view-render.d.ts +2 -0
- package/dist/lib/task-view-render.js +1 -1
- package/dist/lib/views/vocabulary.d.ts +1 -1
- package/dist/lib/views/vocabulary.js +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +1050 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +1196 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +7 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +1050 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +1175 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +7 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +22391 -20727
- package/dist/node_modules/@skrr-ai/data-provider/package.json +1 -1
- package/oclif.manifest.json +14767 -13095
- package/package.json +2 -2
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEDICATED_FILE_MAX_BYTES = exports.DEDICATED_FILE_CHUNK_BYTES = exports.DEDICATED_EXEC_DEFAULT_TIMEOUT_MS = exports.DEDICATED_EXEC_MAX_TIMEOUT_MS = exports.DEDICATED_MACHINE_PRODUCT = exports.DEDICATED_LEASE_ACTIONS = void 0;
|
|
3
|
+
exports.computerMachineId = exports.DEDICATED_FILE_MAX_BYTES = exports.DEDICATED_FILE_CHUNK_BYTES = exports.DEDICATED_EXEC_DEFAULT_TIMEOUT_MS = exports.DEDICATED_EXEC_MAX_TIMEOUT_MS = exports.DEDICATED_MACHINE_PRODUCT = exports.DEDICATED_LEASE_ACTIONS = void 0;
|
|
4
4
|
exports.describeDedicatedCheckpoint = describeDedicatedCheckpoint;
|
|
5
5
|
exports.compareDaemonVersions = compareDaemonVersions;
|
|
6
6
|
exports.describeDedicatedDaemonRelease = describeDedicatedDaemonRelease;
|
|
7
7
|
exports.describeDedicatedImageStanding = describeDedicatedImageStanding;
|
|
8
8
|
exports.dedicatedRuntimeCreateInput = dedicatedRuntimeCreateInput;
|
|
9
9
|
exports.isDedicatedRepositoryRef = isDedicatedRepositoryRef;
|
|
10
|
+
exports.withDedicatedScope = withDedicatedScope;
|
|
10
11
|
exports.createDedicatedRuntime = createDedicatedRuntime;
|
|
11
12
|
exports.getDedicatedOperatorPriceBook = getDedicatedOperatorPriceBook;
|
|
12
13
|
exports.dedicatedOperatorPriceBookLines = dedicatedOperatorPriceBookLines;
|
|
@@ -16,12 +17,16 @@ exports.performDedicatedLeaseAction = performDedicatedLeaseAction;
|
|
|
16
17
|
exports.growDedicatedRuntime = growDedicatedRuntime;
|
|
17
18
|
exports.restoreDeletedDedicatedRuntime = restoreDeletedDedicatedRuntime;
|
|
18
19
|
exports.setDedicatedRuntimeSpendingLimit = setDedicatedRuntimeSpendingLimit;
|
|
20
|
+
exports.getDedicatedRuntimeAgentBrowser = getDedicatedRuntimeAgentBrowser;
|
|
21
|
+
exports.setDedicatedRuntimeAgentBrowser = setDedicatedRuntimeAgentBrowser;
|
|
22
|
+
exports.describeDedicatedAgentBrowser = describeDedicatedAgentBrowser;
|
|
19
23
|
exports.closeDedicatedRuntimeIncidentHold = closeDedicatedRuntimeIncidentHold;
|
|
20
24
|
exports.destroyDedicatedRuntime = destroyDedicatedRuntime;
|
|
21
25
|
exports.execDedicatedRuntime = execDedicatedRuntime;
|
|
22
26
|
exports.getDedicatedRuntimeSignInStatus = getDedicatedRuntimeSignInStatus;
|
|
23
27
|
exports.startDedicatedRuntimeExecution = startDedicatedRuntimeExecution;
|
|
24
28
|
exports.observeDedicatedRuntimeExecution = observeDedicatedRuntimeExecution;
|
|
29
|
+
exports.dedicatedComputerUrl = dedicatedComputerUrl;
|
|
25
30
|
exports.readDedicatedRuntimeFileChunk = readDedicatedRuntimeFileChunk;
|
|
26
31
|
exports.writeDedicatedRuntimeFileChunk = writeDedicatedRuntimeFileChunk;
|
|
27
32
|
exports.parseDedicatedCopyEndpoints = parseDedicatedCopyEndpoints;
|
|
@@ -53,6 +58,7 @@ const auth_core_1 = require("@skrr-ai/auth-core");
|
|
|
53
58
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
54
59
|
const api_fetch_1 = require("./api-fetch");
|
|
55
60
|
const machine_spend_cap_1 = require("./machine-spend-cap");
|
|
61
|
+
const web_url_1 = require("./web-url");
|
|
56
62
|
const edge_error_page_1 = require("./edge-error-page");
|
|
57
63
|
const harnesses_1 = require("./harnesses");
|
|
58
64
|
exports.DEDICATED_LEASE_ACTIONS = [
|
|
@@ -365,6 +371,40 @@ async function restoreDeletedDedicatedRuntime(leaseId, options = {}) {
|
|
|
365
371
|
async function setDedicatedRuntimeSpendingLimit(leaseId, spendingLimitCents, scope = {}) {
|
|
366
372
|
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/leases/${encodeURIComponent(leaseId)}/billing`, scope), { method: 'PATCH', body: { spendingLimitCents } });
|
|
367
373
|
}
|
|
374
|
+
async function getDedicatedRuntimeAgentBrowser(leaseId, scope = {}) {
|
|
375
|
+
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/agent-browser`, scope));
|
|
376
|
+
}
|
|
377
|
+
/** Turn the agents' browser on or off. An actual change is in the machine's audit record. */
|
|
378
|
+
async function setDedicatedRuntimeAgentBrowser(leaseId, enabled, scope = {}) {
|
|
379
|
+
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/agent-browser`, scope), { method: 'PUT', body: { enabled } });
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* One line for a person. Says the bad cases out loud: a setting that is on
|
|
383
|
+
* while the connected daemon does not offer it yet, and a setting we could
|
|
384
|
+
* not read at all.
|
|
385
|
+
*/
|
|
386
|
+
function describeDedicatedAgentBrowser(value, leaseId) {
|
|
387
|
+
if (!value)
|
|
388
|
+
return 'unknown';
|
|
389
|
+
if ('error' in value)
|
|
390
|
+
return `unknown (could not read: ${value.error})`;
|
|
391
|
+
if (value.state === 'pending_first_start') {
|
|
392
|
+
return value.enabled
|
|
393
|
+
? 'on when the machine first starts (the default for new machines)'
|
|
394
|
+
: 'off when the machine first starts';
|
|
395
|
+
}
|
|
396
|
+
if (value.state === 'off') {
|
|
397
|
+
return value.offered
|
|
398
|
+
? `off — sessions already running keep theirs until the daemon reconnects (\`skrr machines dedicated restart ${leaseId}\`)`
|
|
399
|
+
: `off — turn on with \`skrr machines dedicated agent-browser ${leaseId} --on\``;
|
|
400
|
+
}
|
|
401
|
+
if (value.offered === true)
|
|
402
|
+
return 'on — every agent session can open one; you can watch and take it over';
|
|
403
|
+
if (value.offered === false) {
|
|
404
|
+
return `on, but the connected daemon does not offer it yet: it applies when the daemon reconnects (\`skrr machines dedicated restart ${leaseId}\`), and an image without Chrome never offers it`;
|
|
405
|
+
}
|
|
406
|
+
return 'on (not connected now, so whether the machine offers it is unknown)';
|
|
407
|
+
}
|
|
368
408
|
async function closeDedicatedRuntimeIncidentHold(leaseId, options = {}) {
|
|
369
409
|
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/incident-hold/close`, options), {
|
|
370
410
|
method: 'POST',
|
|
@@ -445,11 +485,89 @@ async function observeDedicatedRuntimeExecution(leaseId, executionId, options) {
|
|
|
445
485
|
/** Mirrors the daemon's bounds (daemon/src/tools/file-transfer.ts). */
|
|
446
486
|
exports.DEDICATED_FILE_CHUNK_BYTES = 512 * 1024;
|
|
447
487
|
exports.DEDICATED_FILE_MAX_BYTES = 64 * 1024 * 1024;
|
|
488
|
+
/**
|
|
489
|
+
* `lease:<leaseId>` is the machine id the computer file routes address a
|
|
490
|
+
* Dedicated Runtime by (`/api/machines/:machineId/files/*`, authorised by
|
|
491
|
+
* ComputerAccess), the same id the machine inventory uses.
|
|
492
|
+
*/
|
|
493
|
+
const computerMachineId = (leaseId) => `lease:${leaseId}`;
|
|
494
|
+
exports.computerMachineId = computerMachineId;
|
|
495
|
+
/**
|
|
496
|
+
* The computer's page for a Dedicated Runtime — `/machines/<machineId>/computer`
|
|
497
|
+
* (the route `paneRouteConfig` serves the computer shell at, design §11).
|
|
498
|
+
* Composed on the `machine` web route rather than concatenated against baseURL,
|
|
499
|
+
* so the `api.`-subdomain → apex origin mapping stays the one in web-url.ts.
|
|
500
|
+
*/
|
|
501
|
+
function dedicatedComputerUrl(baseURL, leaseId) {
|
|
502
|
+
const page = (0, web_url_1.webUrl)(baseURL, { kind: 'machine', machineId: (0, exports.computerMachineId)(leaseId) });
|
|
503
|
+
return page ? `${page}/computer` : null;
|
|
504
|
+
}
|
|
505
|
+
const LEGACY_FILE_CODES = {
|
|
506
|
+
file_changed: 'DEDICATED_RUNTIME_FILE_CHANGED',
|
|
507
|
+
too_large: 'DEDICATED_RUNTIME_FILE_TOO_LARGE',
|
|
508
|
+
not_found: 'DEDICATED_RUNTIME_FILE_NOT_FOUND',
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* The copy loop branches on the `DEDICATED_RUNTIME_*` code a refused chunk
|
|
512
|
+
* carried (a destination that is a directory, a file that changed). The file
|
|
513
|
+
* routes answer in the computer's wire vocabulary and keep the specific cause in
|
|
514
|
+
* `details.reason`, so a refusal is re-coded here, at the one place that knows
|
|
515
|
+
* about the copy loop, rather than the loop learning two vocabularies.
|
|
516
|
+
*/
|
|
517
|
+
function withCopyLoopCode(err) {
|
|
518
|
+
if (!(err instanceof api_fetch_1.ApiFetchError))
|
|
519
|
+
return err;
|
|
520
|
+
let reason;
|
|
521
|
+
try {
|
|
522
|
+
reason = JSON.parse(err.body ?? '{}').details?.reason;
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
reason = undefined;
|
|
526
|
+
}
|
|
527
|
+
const code = typeof reason === 'string' && reason.startsWith('DEDICATED_RUNTIME_')
|
|
528
|
+
? reason
|
|
529
|
+
: err.code
|
|
530
|
+
? LEGACY_FILE_CODES[err.code]
|
|
531
|
+
: undefined;
|
|
532
|
+
if (code)
|
|
533
|
+
err.code = code;
|
|
534
|
+
return err;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* One chunk of a download. `generation` is the lease generation the `cp`
|
|
538
|
+
* command resolved at its start, presented on every chunk (review C-01): a
|
|
539
|
+
* machine replaced mid-copy refuses the rest `stale_generation`.
|
|
540
|
+
*/
|
|
448
541
|
async function readDedicatedRuntimeFileChunk(leaseId, input, scope = {}, signal) {
|
|
449
|
-
|
|
542
|
+
const wire = await (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/${encodeURIComponent((0, exports.computerMachineId)(leaseId))}/files/read`, scope), { method: 'POST', body: input, ...(signal ? { signal } : {}) }).catch((err) => {
|
|
543
|
+
throw withCopyLoopCode(err);
|
|
544
|
+
});
|
|
545
|
+
return {
|
|
546
|
+
path: wire.path,
|
|
547
|
+
size: wire.size,
|
|
548
|
+
// The tag's nanosecond mtime, as the whole-millisecond value the copy loop
|
|
549
|
+
// compares between chunks to notice a file that changed under it.
|
|
550
|
+
mtimeMs: Number(BigInt(wire.tag.mtimeNs) / 1000000n),
|
|
551
|
+
...(typeof wire.executable === 'boolean' ? { executable: wire.executable } : {}),
|
|
552
|
+
offset: wire.offset,
|
|
553
|
+
bytesRead: Buffer.from(wire.data, 'base64').byteLength,
|
|
554
|
+
dataBase64: wire.data,
|
|
555
|
+
eof: wire.eof,
|
|
556
|
+
};
|
|
450
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* One call of a chunked upload. `generation` fences every chunk to the machine
|
|
560
|
+
* the command resolved (review C-01). How the commit treats a destination is
|
|
561
|
+
* the caller's explicit choice (review CW-01), never an unconditional replace:
|
|
562
|
+
* `createOnly` commits only if nothing has the name (else 409
|
|
563
|
+
* `invalid_request`/`exists`); `replaceTag` commits only over the file with
|
|
564
|
+
* exactly that tag (else 409 `file_changed` with `currentTag`, null when it is
|
|
565
|
+
* gone). Never both. Each needs `computer_files_fenced_v1` on the machine.
|
|
566
|
+
*/
|
|
451
567
|
async function writeDedicatedRuntimeFileChunk(leaseId, input, scope = {}) {
|
|
452
|
-
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines
|
|
568
|
+
return (0, api_fetch_1.apiFetch)(withDedicatedScope(`/api/machines/${encodeURIComponent((0, exports.computerMachineId)(leaseId))}/files/write`, scope), { method: 'POST', body: input }).catch((err) => {
|
|
569
|
+
throw withCopyLoopCode(err);
|
|
570
|
+
});
|
|
453
571
|
}
|
|
454
572
|
const REMOTE_ENDPOINT = /^(rtl_[A-Za-z0-9_-]{1,80}):(.+)$/;
|
|
455
573
|
/**
|
|
@@ -570,7 +688,8 @@ function dedicatedSpendSummary(lease) {
|
|
|
570
688
|
if (!billing)
|
|
571
689
|
return '-';
|
|
572
690
|
const cap = billing.monthlySpendLimitCents !== undefined
|
|
573
|
-
? billing.monthlySpendLimitCents
|
|
691
|
+
? billing.monthlySpendLimitCents
|
|
692
|
+
: billing.spendingLimitCents;
|
|
574
693
|
if (billing.spendStatusUnavailable) {
|
|
575
694
|
return `spend unavailable right now · ${cap === null ? 'no monthly cap' : `cap ${formatUsdCents(cap)}`}`;
|
|
576
695
|
}
|
|
@@ -647,6 +766,12 @@ function describeDedicatedLease(lease, harnesses = [], opts = {}) {
|
|
|
647
766
|
row('state', lease.stateReason
|
|
648
767
|
? `${dedicatedLeaseState(lease)} (${lease.stateReason})`
|
|
649
768
|
: dedicatedLeaseState(lease));
|
|
769
|
+
// A reason the platform wrote for its own stop gets a sentence, not just a code: the
|
|
770
|
+
// code names an internal rule and leaves the owner asking where their machine went
|
|
771
|
+
// (OSK-13732). A reason with no copy keeps printing as the code above.
|
|
772
|
+
const stateReasonCopy = (0, data_provider_1.describeMachineStateReason)(lease.stateReason);
|
|
773
|
+
if (stateReasonCopy)
|
|
774
|
+
row('why', stateReasonCopy);
|
|
650
775
|
const resources = lease.resources || {};
|
|
651
776
|
const size = [
|
|
652
777
|
resources.sizePreset,
|
|
@@ -725,6 +850,9 @@ function describeDedicatedLease(lease, harnesses = [], opts = {}) {
|
|
|
725
850
|
if (checkpoint)
|
|
726
851
|
lines.push(` ${''.padEnd(13)}${checkpoint.sentence}`);
|
|
727
852
|
}
|
|
853
|
+
if (opts.agentBrowser !== undefined) {
|
|
854
|
+
row('agent browser', describeDedicatedAgentBrowser(opts.agentBrowser, dedicatedLeaseId(lease)));
|
|
855
|
+
}
|
|
728
856
|
const engines = dedicatedLeaseHarnesses(lease, harnesses);
|
|
729
857
|
if (engines.length > 0) {
|
|
730
858
|
row('engines', engines.map((harness) => `${harness.provider} (${harness.status || 'unknown'})`).join(', '));
|
|
@@ -1119,10 +1247,13 @@ function dedicatedCreateSummary(catalog, choices) {
|
|
|
1119
1247
|
: 'server default';
|
|
1120
1248
|
const retains = retentionClass !== undefined && retentionClass !== 'none';
|
|
1121
1249
|
const capCents = choices.spendingLimitCents !== undefined
|
|
1122
|
-
? choices.spendingLimitCents
|
|
1250
|
+
? choices.spendingLimitCents
|
|
1251
|
+
: catalog?.defaultMonthlySpendingLimitCents;
|
|
1123
1252
|
const cap = capCents !== undefined && capCents !== null
|
|
1124
1253
|
? `${formatUsdCents(capCents)} per month${choices.spendingLimitCents === undefined ? ' (catalog default)' : ''}`
|
|
1125
|
-
: capCents === null
|
|
1254
|
+
: capCents === null
|
|
1255
|
+
? 'no cap — compute and retained storage still bill'
|
|
1256
|
+
: 'server default';
|
|
1126
1257
|
return [
|
|
1127
1258
|
`Create Dedicated Runtime "${choices.name}" — it bills while it runs:`,
|
|
1128
1259
|
` size ${choices.sizePreset} — ${spec}`,
|
|
@@ -86,7 +86,9 @@ export type RuntimeBinaryOutput = {
|
|
|
86
86
|
* post-condition probes, where output is evidence rather than user-facing
|
|
87
87
|
* command output.
|
|
88
88
|
*/
|
|
89
|
-
export declare function execRuntimeBinaryCapture(args: string[]
|
|
89
|
+
export declare function execRuntimeBinaryCapture(args: string[], opts?: {
|
|
90
|
+
timeoutMs?: number;
|
|
91
|
+
}): Promise<RuntimeBinaryOutput>;
|
|
90
92
|
/**
|
|
91
93
|
* Check an installed daemon's top-level help before forwarding a command that
|
|
92
94
|
* was added after older daemon releases shipped. A false result is useful to
|
|
@@ -251,7 +251,7 @@ function execRuntimeBinary(args, opts = {}) {
|
|
|
251
251
|
* post-condition probes, where output is evidence rather than user-facing
|
|
252
252
|
* command output.
|
|
253
253
|
*/
|
|
254
|
-
function execRuntimeBinaryCapture(args) {
|
|
254
|
+
function execRuntimeBinaryCapture(args, opts = {}) {
|
|
255
255
|
return new Promise((resolve, reject) => {
|
|
256
256
|
const bin = findRuntimeBinary();
|
|
257
257
|
if (!bin) {
|
|
@@ -279,8 +279,22 @@ function execRuntimeBinaryCapture(args) {
|
|
|
279
279
|
child.stderr?.on('data', (chunk) => {
|
|
280
280
|
stderr = append(stderr, chunk);
|
|
281
281
|
});
|
|
282
|
-
|
|
282
|
+
// A probe is a read: past its bound it is abandoned (and the child
|
|
283
|
+
// stopped) rather than allowed to stall the command that asked.
|
|
284
|
+
const timer = opts.timeoutMs && opts.timeoutMs > 0
|
|
285
|
+
? setTimeout(() => {
|
|
286
|
+
child.kill('SIGTERM');
|
|
287
|
+
reject(new Error('runtime-binary-probe-timeout'));
|
|
288
|
+
}, opts.timeoutMs)
|
|
289
|
+
: null;
|
|
290
|
+
child.on('error', (error) => {
|
|
291
|
+
if (timer)
|
|
292
|
+
clearTimeout(timer);
|
|
293
|
+
reject(error);
|
|
294
|
+
});
|
|
283
295
|
child.on('exit', (code, signal) => {
|
|
296
|
+
if (timer)
|
|
297
|
+
clearTimeout(timer);
|
|
284
298
|
resolve({
|
|
285
299
|
code: signal ? 128 + signalNumber(signal) : (code ?? 0),
|
|
286
300
|
stdout,
|
|
@@ -39,6 +39,18 @@ export declare const labelScopeFlags: {
|
|
|
39
39
|
scope: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
40
40
|
'scope-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* `--allow-duplicate-name`, shared by every command that can put a live name
|
|
44
|
+
* into a scope (create, rename, restore).
|
|
45
|
+
*
|
|
46
|
+
* The SERVER owns the rule (`LabelService.assertNameFreeInOtherScope`): a name
|
|
47
|
+
* another label you can use already carries is refused with
|
|
48
|
+
* `409 duplicate_name_other_scope`, and the refusal names that label. The CLI
|
|
49
|
+
* only relays the override, so every surface — web, MCP, CLI — gets one answer.
|
|
50
|
+
*/
|
|
51
|
+
export declare const allowDuplicateNameFlags: {
|
|
52
|
+
'allow-duplicate-name': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
53
|
+
};
|
|
42
54
|
export interface ResolvedLabelScope {
|
|
43
55
|
scope: LabelScope;
|
|
44
56
|
scopeId?: string;
|
package/dist/lib/label-scope.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
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_INFERRED = exports.LABEL_SCOPES_REQUIRING_ID = exports.LABEL_SCOPES = void 0;
|
|
14
|
+
exports.allowDuplicateNameFlags = 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;
|
|
@@ -56,6 +56,20 @@ exports.labelScopeFlags = {
|
|
|
56
56
|
description: `Scope owner ID — required for ${joinScopes(scopesRequiringId())}. Omit for ${joinScopes(exports.LABEL_SCOPES_INFERRED)} (defaults to you).`,
|
|
57
57
|
}),
|
|
58
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* `--allow-duplicate-name`, shared by every command that can put a live name
|
|
61
|
+
* into a scope (create, rename, restore).
|
|
62
|
+
*
|
|
63
|
+
* The SERVER owns the rule (`LabelService.assertNameFreeInOtherScope`): a name
|
|
64
|
+
* another label you can use already carries is refused with
|
|
65
|
+
* `409 duplicate_name_other_scope`, and the refusal names that label. The CLI
|
|
66
|
+
* only relays the override, so every surface — web, MCP, CLI — gets one answer.
|
|
67
|
+
*/
|
|
68
|
+
exports.allowDuplicateNameFlags = {
|
|
69
|
+
'allow-duplicate-name': core_1.Flags.boolean({
|
|
70
|
+
description: 'Proceed even though another label you can use (your user scope, or a workspace you belong to) already has this name. Refused by default: two labels with one name make `--label <name>` ambiguous.',
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
59
73
|
/**
|
|
60
74
|
* Validate the scope pair. Throws a plain `Error` so callers can surface it
|
|
61
75
|
* through `this.error(..., { exit: 1 })` with the rest of their flag checks.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { COMPUTER_AUDIT_KINDS, type ComputerAuditPage } from '@skrr-ai/data-provider';
|
|
2
|
+
/**
|
|
3
|
+
* machine-audit.ts — request client for `GET /api/machines/:machineId/audit`
|
|
4
|
+
* (design §9, D8/D10; OSK-13607): who did what on a machine, after the
|
|
5
|
+
* daemon's live Activity window has rolled off.
|
|
6
|
+
*
|
|
7
|
+
* The owner reads it always; a grantee at any tier reads it too, without the
|
|
8
|
+
* `grant_*` events (those name the other people the machine is shared with).
|
|
9
|
+
* Vocabulary and the page shape are owned by `@skrr-ai/data-provider`.
|
|
10
|
+
*
|
|
11
|
+
* ## Inline path literal
|
|
12
|
+
*
|
|
13
|
+
* `domain-api-parity` resolves a command to an endpoint by reading this
|
|
14
|
+
* string; a helper that assembled the URL would read as unreachable.
|
|
15
|
+
*/
|
|
16
|
+
export { COMPUTER_AUDIT_KINDS };
|
|
17
|
+
export type { ComputerAuditPage };
|
|
18
|
+
export interface MachineAuditQuery {
|
|
19
|
+
kinds?: string[];
|
|
20
|
+
actorUserId?: string;
|
|
21
|
+
/** ISO time or epoch ms — occurredAt >= since. */
|
|
22
|
+
since?: string | number;
|
|
23
|
+
/** ISO time or epoch ms — occurredAt < until. */
|
|
24
|
+
until?: string | number;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
limit?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A `--since` / `--until` value as an ISO time: an absolute time as typed
|
|
30
|
+
* (`2026-09-24`, `2026-09-24T10:00:00Z`), or an age such as `90m`, `12h`,
|
|
31
|
+
* `7d`, `2w` counted back from `now`. Throws on anything else, naming the flag.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseAuditTime(value: string, flagName: string, now?: number): string;
|
|
34
|
+
export declare function listMachineAudit(machineId: string, query?: MachineAuditQuery): Promise<ComputerAuditPage>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMPUTER_AUDIT_KINDS = void 0;
|
|
4
|
+
exports.parseAuditTime = parseAuditTime;
|
|
5
|
+
exports.listMachineAudit = listMachineAudit;
|
|
6
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
7
|
+
Object.defineProperty(exports, "COMPUTER_AUDIT_KINDS", { enumerable: true, get: function () { return data_provider_1.COMPUTER_AUDIT_KINDS; } });
|
|
8
|
+
const api_fetch_1 = require("./api-fetch");
|
|
9
|
+
const DURATION_MS = {
|
|
10
|
+
s: 1000,
|
|
11
|
+
m: 60_000,
|
|
12
|
+
h: 3_600_000,
|
|
13
|
+
d: 86_400_000,
|
|
14
|
+
w: 7 * 86_400_000,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A `--since` / `--until` value as an ISO time: an absolute time as typed
|
|
18
|
+
* (`2026-09-24`, `2026-09-24T10:00:00Z`), or an age such as `90m`, `12h`,
|
|
19
|
+
* `7d`, `2w` counted back from `now`. Throws on anything else, naming the flag.
|
|
20
|
+
*/
|
|
21
|
+
function parseAuditTime(value, flagName, now = Date.now()) {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
const age = trimmed.match(/^(\d+)([smhdw])$/i);
|
|
24
|
+
if (age) {
|
|
25
|
+
return new Date(now - Number(age[1]) * DURATION_MS[age[2].toLowerCase()]).toISOString();
|
|
26
|
+
}
|
|
27
|
+
const absolute = new Date(trimmed);
|
|
28
|
+
if (!trimmed || Number.isNaN(absolute.getTime())) {
|
|
29
|
+
throw new Error(`Invalid ${flagName} value: ${value}. Use a time such as 2026-09-24 or an age such as 7d.`);
|
|
30
|
+
}
|
|
31
|
+
return absolute.toISOString();
|
|
32
|
+
}
|
|
33
|
+
async function listMachineAudit(machineId, query = {}) {
|
|
34
|
+
const params = new URLSearchParams();
|
|
35
|
+
for (const kind of query.kinds ?? [])
|
|
36
|
+
params.append('kind', kind);
|
|
37
|
+
if (query.actorUserId)
|
|
38
|
+
params.set('actor', query.actorUserId);
|
|
39
|
+
if (query.since != null)
|
|
40
|
+
params.set('since', String(query.since));
|
|
41
|
+
if (query.until != null)
|
|
42
|
+
params.set('until', String(query.until));
|
|
43
|
+
if (query.cursor)
|
|
44
|
+
params.set('cursor', query.cursor);
|
|
45
|
+
if (query.limit != null)
|
|
46
|
+
params.set('limit', String(query.limit));
|
|
47
|
+
const qs = params.toString();
|
|
48
|
+
const suffix = qs ? `?${qs}` : '';
|
|
49
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/audit` + suffix);
|
|
50
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* machine-grants.ts — request client for `/api/machines/:machineId/grants`
|
|
3
|
+
* (design §9, D10; OSK-13558): who the owner shares a machine with.
|
|
4
|
+
*
|
|
5
|
+
* A grant gives one person, on one machine, one of two fixed tiers — `watch`
|
|
6
|
+
* (see every surface and its activity; no input, no file browsing) or
|
|
7
|
+
* `operate` (everything watch has, plus control and file read/write). `power`,
|
|
8
|
+
* closing other people's surfaces and grant management stay with the owner,
|
|
9
|
+
* which is why every verb here resolves machine OWNERSHIP server-side rather
|
|
10
|
+
* than checking a grant. The grantee may be any user, inside the owner's
|
|
11
|
+
* workspace or not.
|
|
12
|
+
*
|
|
13
|
+
* ## Inline path literals
|
|
14
|
+
*
|
|
15
|
+
* `domain-api-parity` resolves a command to an endpoint by reading these
|
|
16
|
+
* strings. A helper that assembles the URL is opaque to it, so the endpoint
|
|
17
|
+
* would read as unreachable and get allow-listed — a check going green on an
|
|
18
|
+
* assertion it never made.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MACHINE_GRANT_TIERS: readonly ["watch", "operate"];
|
|
21
|
+
export type MachineGrantTier = (typeof MACHINE_GRANT_TIERS)[number];
|
|
22
|
+
/** One grant, as `ComputerGrantService.publicGrant` emits it. */
|
|
23
|
+
export interface MachineGrant {
|
|
24
|
+
grantId: string;
|
|
25
|
+
machineId: string;
|
|
26
|
+
ownerUserId: string;
|
|
27
|
+
granteeUserId: string;
|
|
28
|
+
tier: MachineGrantTier;
|
|
29
|
+
createdAt?: number;
|
|
30
|
+
updatedAt?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface MachineGrantList {
|
|
33
|
+
version: number;
|
|
34
|
+
grants: MachineGrant[];
|
|
35
|
+
}
|
|
36
|
+
export interface MachineGrantMutation {
|
|
37
|
+
grant: MachineGrant;
|
|
38
|
+
version: number;
|
|
39
|
+
/** POST only: false when the person already held this exact grant. */
|
|
40
|
+
created?: boolean;
|
|
41
|
+
/** PATCH only: false when the grant already held this tier. */
|
|
42
|
+
changed?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface MachineGrantRevocation {
|
|
45
|
+
revoked: MachineGrant;
|
|
46
|
+
version: number;
|
|
47
|
+
}
|
|
48
|
+
export declare function listMachineGrants(machineId: string): Promise<MachineGrantList>;
|
|
49
|
+
export declare function shareMachine(machineId: string, granteeUserId: string, tier: MachineGrantTier): Promise<MachineGrantMutation>;
|
|
50
|
+
export declare function changeMachineGrant(machineId: string, granteeUserId: string, tier: MachineGrantTier): Promise<MachineGrantMutation>;
|
|
51
|
+
export declare function unshareMachine(machineId: string, granteeUserId: string): Promise<MachineGrantRevocation>;
|
|
52
|
+
export interface SharedMachine {
|
|
53
|
+
machineId: string;
|
|
54
|
+
name: string | null;
|
|
55
|
+
owner: {
|
|
56
|
+
userId: string;
|
|
57
|
+
name: string;
|
|
58
|
+
username?: string;
|
|
59
|
+
avatar?: string;
|
|
60
|
+
} | null;
|
|
61
|
+
tier: MachineGrantTier;
|
|
62
|
+
grantedAt?: number;
|
|
63
|
+
power?: {
|
|
64
|
+
phase: string;
|
|
65
|
+
} | null;
|
|
66
|
+
}
|
|
67
|
+
export interface SharedMachineList {
|
|
68
|
+
machines: SharedMachine[];
|
|
69
|
+
}
|
|
70
|
+
export declare function listSharedMachines(): Promise<SharedMachineList>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MACHINE_GRANT_TIERS = void 0;
|
|
4
|
+
exports.listMachineGrants = listMachineGrants;
|
|
5
|
+
exports.shareMachine = shareMachine;
|
|
6
|
+
exports.changeMachineGrant = changeMachineGrant;
|
|
7
|
+
exports.unshareMachine = unshareMachine;
|
|
8
|
+
exports.listSharedMachines = listSharedMachines;
|
|
9
|
+
const api_fetch_1 = require("./api-fetch");
|
|
10
|
+
/**
|
|
11
|
+
* machine-grants.ts — request client for `/api/machines/:machineId/grants`
|
|
12
|
+
* (design §9, D10; OSK-13558): who the owner shares a machine with.
|
|
13
|
+
*
|
|
14
|
+
* A grant gives one person, on one machine, one of two fixed tiers — `watch`
|
|
15
|
+
* (see every surface and its activity; no input, no file browsing) or
|
|
16
|
+
* `operate` (everything watch has, plus control and file read/write). `power`,
|
|
17
|
+
* closing other people's surfaces and grant management stay with the owner,
|
|
18
|
+
* which is why every verb here resolves machine OWNERSHIP server-side rather
|
|
19
|
+
* than checking a grant. The grantee may be any user, inside the owner's
|
|
20
|
+
* workspace or not.
|
|
21
|
+
*
|
|
22
|
+
* ## Inline path literals
|
|
23
|
+
*
|
|
24
|
+
* `domain-api-parity` resolves a command to an endpoint by reading these
|
|
25
|
+
* strings. A helper that assembles the URL is opaque to it, so the endpoint
|
|
26
|
+
* would read as unreachable and get allow-listed — a check going green on an
|
|
27
|
+
* assertion it never made.
|
|
28
|
+
*/
|
|
29
|
+
exports.MACHINE_GRANT_TIERS = ['watch', 'operate'];
|
|
30
|
+
async function listMachineGrants(machineId) {
|
|
31
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/grants`);
|
|
32
|
+
}
|
|
33
|
+
async function shareMachine(machineId, granteeUserId, tier) {
|
|
34
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/grants`, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: { granteeUserId, tier },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function changeMachineGrant(machineId, granteeUserId, tier) {
|
|
40
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/grants/${encodeURIComponent(granteeUserId)}`, { method: 'PATCH', body: { tier } });
|
|
41
|
+
}
|
|
42
|
+
async function unshareMachine(machineId, granteeUserId) {
|
|
43
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/grants/${encodeURIComponent(granteeUserId)}`, { method: 'DELETE' });
|
|
44
|
+
}
|
|
45
|
+
async function listSharedMachines() {
|
|
46
|
+
return (0, api_fetch_1.apiFetch)('/api/machines/shared-with-me');
|
|
47
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* machine-services.ts — request client for `/api/machines/:machineId/services`
|
|
3
|
+
* (design §20.5; 9-17, OSK-13579): a machine's declared services, the web
|
|
4
|
+
* services an agent or a person started on it, named so the computer can open
|
|
5
|
+
* them in its own browser.
|
|
6
|
+
*
|
|
7
|
+
* Not the same thing as `skrr machines dedicated service`: those are always-on
|
|
8
|
+
* WORKLOADS a Dedicated Runtime keeps running. A declared service is an
|
|
9
|
+
* address — a port on the machine's own loopback, and an optional path — on
|
|
10
|
+
* any computer, a personal machine's included. Declaring one starts nothing.
|
|
11
|
+
*
|
|
12
|
+
* Who may: the owner and an `operate` grantee declare and withdraw; a `watch`
|
|
13
|
+
* grantee may list. The server decides (`ComputerAccess`), and so does the
|
|
14
|
+
* machine's daemon, against the grant set it holds.
|
|
15
|
+
*
|
|
16
|
+
* ## Inline path literals
|
|
17
|
+
*
|
|
18
|
+
* `domain-api-parity` resolves a command to an endpoint by reading these
|
|
19
|
+
* strings, so each one is written out here rather than assembled by a helper.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DECLARED_SERVICE_PROTOCOLS: readonly ["http", "https"];
|
|
22
|
+
export type DeclaredServiceProtocol = (typeof DECLARED_SERVICE_PROTOCOLS)[number];
|
|
23
|
+
export type DeclaredServiceActor = {
|
|
24
|
+
kind: 'agent';
|
|
25
|
+
agentId: string;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
label: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'human';
|
|
30
|
+
userId: string;
|
|
31
|
+
label: string;
|
|
32
|
+
};
|
|
33
|
+
/** One declared service, as the machine's manifest carries it. */
|
|
34
|
+
export interface DeclaredService {
|
|
35
|
+
name: string;
|
|
36
|
+
port: number;
|
|
37
|
+
path: string;
|
|
38
|
+
protocol: DeclaredServiceProtocol;
|
|
39
|
+
declaredBy: DeclaredServiceActor;
|
|
40
|
+
declaredAt: number;
|
|
41
|
+
}
|
|
42
|
+
export interface DeclaredServicesResult {
|
|
43
|
+
services: DeclaredService[];
|
|
44
|
+
service?: DeclaredService;
|
|
45
|
+
replaced?: boolean;
|
|
46
|
+
withdrawn?: DeclaredService;
|
|
47
|
+
}
|
|
48
|
+
/** Where a service opens: always the machine's own loopback. */
|
|
49
|
+
export declare function declaredServiceAddress(service: Pick<DeclaredService, 'port' | 'path' | 'protocol'>): string;
|
|
50
|
+
export declare function listMachineServices(machineId: string): Promise<DeclaredServicesResult>;
|
|
51
|
+
export declare function declareMachineService(machineId: string, service: {
|
|
52
|
+
name: string;
|
|
53
|
+
port: number;
|
|
54
|
+
path?: string;
|
|
55
|
+
protocol?: DeclaredServiceProtocol;
|
|
56
|
+
}): Promise<DeclaredServicesResult>;
|
|
57
|
+
export declare function withdrawMachineService(machineId: string, name: string): Promise<DeclaredServicesResult>;
|
|
58
|
+
/**
|
|
59
|
+
* The machine id a services command addresses. A machine the person owns
|
|
60
|
+
* resolves from their inventory by any of its names; a machine shared WITH
|
|
61
|
+
* them is not in that inventory, so a full machine id (`lease:…`,
|
|
62
|
+
* `daemon:…`, `machine:…`) is taken as written — the server decides access.
|
|
63
|
+
*/
|
|
64
|
+
export declare function servicesMachineId(ref: string): Promise<string>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DECLARED_SERVICE_PROTOCOLS = void 0;
|
|
4
|
+
exports.declaredServiceAddress = declaredServiceAddress;
|
|
5
|
+
exports.listMachineServices = listMachineServices;
|
|
6
|
+
exports.declareMachineService = declareMachineService;
|
|
7
|
+
exports.withdrawMachineService = withdrawMachineService;
|
|
8
|
+
exports.servicesMachineId = servicesMachineId;
|
|
9
|
+
const api_fetch_1 = require("./api-fetch");
|
|
10
|
+
const machines_1 = require("./machines");
|
|
11
|
+
/**
|
|
12
|
+
* machine-services.ts — request client for `/api/machines/:machineId/services`
|
|
13
|
+
* (design §20.5; 9-17, OSK-13579): a machine's declared services, the web
|
|
14
|
+
* services an agent or a person started on it, named so the computer can open
|
|
15
|
+
* them in its own browser.
|
|
16
|
+
*
|
|
17
|
+
* Not the same thing as `skrr machines dedicated service`: those are always-on
|
|
18
|
+
* WORKLOADS a Dedicated Runtime keeps running. A declared service is an
|
|
19
|
+
* address — a port on the machine's own loopback, and an optional path — on
|
|
20
|
+
* any computer, a personal machine's included. Declaring one starts nothing.
|
|
21
|
+
*
|
|
22
|
+
* Who may: the owner and an `operate` grantee declare and withdraw; a `watch`
|
|
23
|
+
* grantee may list. The server decides (`ComputerAccess`), and so does the
|
|
24
|
+
* machine's daemon, against the grant set it holds.
|
|
25
|
+
*
|
|
26
|
+
* ## Inline path literals
|
|
27
|
+
*
|
|
28
|
+
* `domain-api-parity` resolves a command to an endpoint by reading these
|
|
29
|
+
* strings, so each one is written out here rather than assembled by a helper.
|
|
30
|
+
*/
|
|
31
|
+
exports.DECLARED_SERVICE_PROTOCOLS = ['http', 'https'];
|
|
32
|
+
/** Where a service opens: always the machine's own loopback. */
|
|
33
|
+
function declaredServiceAddress(service) {
|
|
34
|
+
return `${service.protocol}://localhost:${service.port}${service.path}`;
|
|
35
|
+
}
|
|
36
|
+
async function listMachineServices(machineId) {
|
|
37
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/services`);
|
|
38
|
+
}
|
|
39
|
+
async function declareMachineService(machineId, service) {
|
|
40
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/services`, { method: 'POST', body: { service } });
|
|
41
|
+
}
|
|
42
|
+
async function withdrawMachineService(machineId, name) {
|
|
43
|
+
return (0, api_fetch_1.apiFetch)(`/api/machines/${encodeURIComponent(machineId)}/services/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The machine id a services command addresses. A machine the person owns
|
|
47
|
+
* resolves from their inventory by any of its names; a machine shared WITH
|
|
48
|
+
* them is not in that inventory, so a full machine id (`lease:…`,
|
|
49
|
+
* `daemon:…`, `machine:…`) is taken as written — the server decides access.
|
|
50
|
+
*/
|
|
51
|
+
async function servicesMachineId(ref) {
|
|
52
|
+
try {
|
|
53
|
+
return (0, machines_1.resolveMachineRef)(await (0, machines_1.listMachines)(), ref).id;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (/^(lease|daemon|machine|stable-daemon):\S+$/.test(ref))
|
|
57
|
+
return ref;
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|