@skrr-ai/cli 0.1.108 → 0.1.110
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 +74 -0
- package/dist/base-command.js +182 -24
- package/dist/commands/agents/triggers/create.js +1 -1
- package/dist/commands/agents/triggers/update.js +1 -1
- package/dist/commands/agents/update.d.ts +25 -0
- package/dist/commands/agents/update.js +98 -3
- 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/daemon/install.js +17 -1
- package/dist/commands/daemon/login.js +14 -0
- 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/harnesses/prune.d.ts +26 -0
- package/dist/commands/harnesses/prune.js +172 -0
- package/dist/commands/login.js +53 -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 +4 -1
- 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 +27 -2
- package/dist/commands/tasks/activity.js +17 -3
- 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 +25 -3
- package/dist/commands/tasks/rearm-recurrence.d.ts +22 -0
- package/dist/commands/tasks/rearm-recurrence.js +87 -0
- package/dist/commands/tasks/show.d.ts +9 -0
- package/dist/commands/tasks/show.js +79 -33
- package/dist/commands/tasks/timeline.js +4 -0
- package/dist/commands/tasks/unschedule.js +5 -1
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +17 -2
- package/dist/commands/triggers/audit.js +12 -3
- package/dist/commands/triggers/create.js +9 -3
- package/dist/commands/triggers/fire-detail.d.ts +7 -0
- package/dist/commands/triggers/fire-detail.js +30 -12
- package/dist/commands/triggers/fire.js +13 -3
- package/dist/commands/triggers/parse-intent.d.ts +7 -0
- package/dist/commands/triggers/parse-intent.js +31 -9
- package/dist/commands/triggers/update.js +1 -1
- package/dist/commands/workspaces/grants.js +20 -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 +5 -2
- package/dist/lib/commitments.d.ts +18 -3
- package/dist/lib/commitments.js +85 -45
- package/dist/lib/daemon-command-hint.d.ts +15 -0
- package/dist/lib/daemon-command-hint.js +21 -0
- package/dist/lib/daemon-machine-identity-copy.d.ts +14 -0
- package/dist/lib/daemon-machine-identity-copy.js +27 -0
- package/dist/lib/daemon-machine-identity.d.ts +27 -0
- package/dist/lib/daemon-machine-identity.js +61 -0
- package/dist/lib/daemon-setup.js +10 -1
- package/dist/lib/daemonBrokerRefusal.js +3 -2
- package/dist/lib/daemonHandoff.d.ts +19 -1
- package/dist/lib/daemonHandoff.js +61 -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/harnesses.d.ts +71 -0
- package/dist/lib/harnesses.js +70 -0
- 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 +10 -1
- package/dist/lib/list-output.js +22 -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 +39 -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 +15 -0
- package/dist/lib/task-economics.js +39 -2
- 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 +42 -1
- package/dist/lib/triggers.js +98 -2
- 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 +59 -0
- package/dist/lib/workflow-run-view.js +110 -0
- package/dist/lib/workspaces.d.ts +48 -4
- package/dist/lib/workspaces.js +55 -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/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +5 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.d.ts +60 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +130 -6
- 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/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.d.ts +60 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +128 -6
- 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 +7523 -7140
- package/oclif.manifest.json +473 -59
- package/package.json +28 -1
package/dist/lib/tasks.d.ts
CHANGED
|
@@ -241,6 +241,45 @@ export declare function findTaskByExternalKey({ externalKey, spaceId, goalId, pa
|
|
|
241
241
|
task: TaskRecord | null;
|
|
242
242
|
duplicates: TaskRecord[];
|
|
243
243
|
}>;
|
|
244
|
+
export type ExternalKeyLookup = {
|
|
245
|
+
externalKey: string;
|
|
246
|
+
spaceId?: unknown;
|
|
247
|
+
goalId?: unknown;
|
|
248
|
+
};
|
|
249
|
+
export type ExternalKeyHit = {
|
|
250
|
+
task: TaskRecord | null;
|
|
251
|
+
duplicates: TaskRecord[];
|
|
252
|
+
/** Set when the lookup itself failed: "could not look" is not "nothing there". */
|
|
253
|
+
error?: Error;
|
|
254
|
+
};
|
|
255
|
+
/** The identity of one lookup: a key means a task only within its scope. */
|
|
256
|
+
export declare function externalKeyLookupId(lookup: ExternalKeyLookup): string;
|
|
257
|
+
/**
|
|
258
|
+
* Which of these tasks already exist? Answers for many keys at once.
|
|
259
|
+
*
|
|
260
|
+
* `create-tree`, `--resume` and `--verify` all start by asking that of every node
|
|
261
|
+
* of a tree, and asked it once per node, one after another: 94 round trips
|
|
262
|
+
* before the first write (OSK-S-440 #3). Two things bring that down without
|
|
263
|
+
* changing a single answer:
|
|
264
|
+
*
|
|
265
|
+
* - Keys that share a prefix (`aap/c1`, `aap/c2`, ... — a tree is written
|
|
266
|
+
* under one) are answered by ONE paged read of the prefix, per scope. The
|
|
267
|
+
* read can return rows that are not this tree's; they are ignored, since a
|
|
268
|
+
* key is matched whole. If the prefix is broad enough that the read hits its
|
|
269
|
+
* page cap, whatever it did not find is looked up directly, because absence
|
|
270
|
+
* from a truncated read proves nothing.
|
|
271
|
+
* - Whatever is left is looked up by exact key, a few at a time rather than
|
|
272
|
+
* one at a time.
|
|
273
|
+
*
|
|
274
|
+
* A server that predates `externalKeyPrefix` ignores it and returns the scope's
|
|
275
|
+
* tasks: still correct (matched whole), merely a wider read, and bounded by the
|
|
276
|
+
* same cap.
|
|
277
|
+
*/
|
|
278
|
+
export declare function findTasksByExternalKeys(lookups: readonly ExternalKeyLookup[], opts?: {
|
|
279
|
+
concurrency?: number;
|
|
280
|
+
minKeysForPrefix?: number;
|
|
281
|
+
maxPages?: number;
|
|
282
|
+
}): Promise<Map<string, ExternalKeyHit>>;
|
|
244
283
|
/**
|
|
245
284
|
* What the CLI reports under `defaultAgent` in `--json` output.
|
|
246
285
|
*
|
package/dist/lib/tasks.js
CHANGED
|
@@ -21,6 +21,8 @@ exports.applyProspectiveTaskExecutionMode = applyProspectiveTaskExecutionMode;
|
|
|
21
21
|
exports.validateTaskCreateReferences = validateTaskCreateReferences;
|
|
22
22
|
exports.resolveTaskCreateScope = resolveTaskCreateScope;
|
|
23
23
|
exports.findTaskByExternalKey = findTaskByExternalKey;
|
|
24
|
+
exports.externalKeyLookupId = externalKeyLookupId;
|
|
25
|
+
exports.findTasksByExternalKeys = findTasksByExternalKeys;
|
|
24
26
|
exports.droppedDefaultAssignees = droppedDefaultAssignees;
|
|
25
27
|
exports.applyTaskAssigneeDefaults = applyTaskAssigneeDefaults;
|
|
26
28
|
exports.hasExplicitTaskAssignee = hasExplicitTaskAssignee;
|
|
@@ -622,6 +624,136 @@ async function findTaskByExternalKey({ externalKey, spaceId, goalId, parentId, }
|
|
|
622
624
|
}
|
|
623
625
|
return { task: items[0] ?? null, duplicates: [] };
|
|
624
626
|
}
|
|
627
|
+
/** The identity of one lookup: a key means a task only within its scope. */
|
|
628
|
+
function externalKeyLookupId(lookup) {
|
|
629
|
+
const scope = (value) => (typeof value === 'string' ? value : '');
|
|
630
|
+
return `${scope(lookup.spaceId)}|${scope(lookup.goalId)}|${lookup.externalKey}`;
|
|
631
|
+
}
|
|
632
|
+
function longestCommonPrefix(values) {
|
|
633
|
+
if (values.length === 0)
|
|
634
|
+
return '';
|
|
635
|
+
let prefix = values[0];
|
|
636
|
+
for (const value of values) {
|
|
637
|
+
let i = 0;
|
|
638
|
+
while (i < prefix.length && i < value.length && prefix[i] === value[i])
|
|
639
|
+
i += 1;
|
|
640
|
+
prefix = prefix.slice(0, i);
|
|
641
|
+
if (!prefix)
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
return prefix;
|
|
645
|
+
}
|
|
646
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
647
|
+
const results = new Array(items.length);
|
|
648
|
+
let next = 0;
|
|
649
|
+
const worker = async () => {
|
|
650
|
+
while (next < items.length) {
|
|
651
|
+
const index = next;
|
|
652
|
+
next += 1;
|
|
653
|
+
results[index] = await fn(items[index]);
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
657
|
+
return results;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Which of these tasks already exist? Answers for many keys at once.
|
|
661
|
+
*
|
|
662
|
+
* `create-tree`, `--resume` and `--verify` all start by asking that of every node
|
|
663
|
+
* of a tree, and asked it once per node, one after another: 94 round trips
|
|
664
|
+
* before the first write (OSK-S-440 #3). Two things bring that down without
|
|
665
|
+
* changing a single answer:
|
|
666
|
+
*
|
|
667
|
+
* - Keys that share a prefix (`aap/c1`, `aap/c2`, ... — a tree is written
|
|
668
|
+
* under one) are answered by ONE paged read of the prefix, per scope. The
|
|
669
|
+
* read can return rows that are not this tree's; they are ignored, since a
|
|
670
|
+
* key is matched whole. If the prefix is broad enough that the read hits its
|
|
671
|
+
* page cap, whatever it did not find is looked up directly, because absence
|
|
672
|
+
* from a truncated read proves nothing.
|
|
673
|
+
* - Whatever is left is looked up by exact key, a few at a time rather than
|
|
674
|
+
* one at a time.
|
|
675
|
+
*
|
|
676
|
+
* A server that predates `externalKeyPrefix` ignores it and returns the scope's
|
|
677
|
+
* tasks: still correct (matched whole), merely a wider read, and bounded by the
|
|
678
|
+
* same cap.
|
|
679
|
+
*/
|
|
680
|
+
async function findTasksByExternalKeys(lookups, opts = {}) {
|
|
681
|
+
const concurrency = opts.concurrency ?? 6;
|
|
682
|
+
const minKeysForPrefix = opts.minKeysForPrefix ?? 8;
|
|
683
|
+
const maxPages = opts.maxPages ?? 10;
|
|
684
|
+
const hits = new Map();
|
|
685
|
+
const unique = new Map();
|
|
686
|
+
for (const lookup of lookups)
|
|
687
|
+
unique.set(externalKeyLookupId(lookup), lookup);
|
|
688
|
+
const byScope = new Map();
|
|
689
|
+
for (const lookup of unique.values()) {
|
|
690
|
+
const scopeId = externalKeyLookupId({ ...lookup, externalKey: '' });
|
|
691
|
+
byScope.set(scopeId, [...(byScope.get(scopeId) ?? []), lookup]);
|
|
692
|
+
}
|
|
693
|
+
const settle = (lookup, rows) => {
|
|
694
|
+
const matching = rows.filter((row) => row.externalKey === lookup.externalKey);
|
|
695
|
+
hits.set(externalKeyLookupId(lookup), {
|
|
696
|
+
task: matching.length === 1 ? matching[0] : null,
|
|
697
|
+
duplicates: matching.length > 1 ? matching : [],
|
|
698
|
+
});
|
|
699
|
+
};
|
|
700
|
+
for (const group of byScope.values()) {
|
|
701
|
+
let unresolved = group;
|
|
702
|
+
const prefix = longestCommonPrefix(group.map((lookup) => lookup.externalKey));
|
|
703
|
+
if (group.length >= minKeysForPrefix && prefix.length >= 3) {
|
|
704
|
+
try {
|
|
705
|
+
const rows = [];
|
|
706
|
+
let cursor;
|
|
707
|
+
let complete = false;
|
|
708
|
+
for (let page = 0; page < maxPages; page += 1) {
|
|
709
|
+
const response = (await data_provider_1.dataService.listTasks({
|
|
710
|
+
externalKeyPrefix: prefix,
|
|
711
|
+
...(typeof group[0].spaceId === 'string' && group[0].spaceId
|
|
712
|
+
? { spaceId: group[0].spaceId }
|
|
713
|
+
: {}),
|
|
714
|
+
...(typeof group[0].goalId === 'string' && group[0].goalId
|
|
715
|
+
? { goalId: group[0].goalId }
|
|
716
|
+
: {}),
|
|
717
|
+
limit: 200,
|
|
718
|
+
...(cursor ? { cursor } : {}),
|
|
719
|
+
}));
|
|
720
|
+
rows.push(...listItems(response));
|
|
721
|
+
if (!response?.has_more || !response.next_cursor) {
|
|
722
|
+
complete = true;
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
cursor = response.next_cursor;
|
|
726
|
+
}
|
|
727
|
+
unresolved = [];
|
|
728
|
+
for (const lookup of group) {
|
|
729
|
+
const found = rows.some((row) => row.externalKey === lookup.externalKey);
|
|
730
|
+
// Absence is only an answer from a read that reached the end.
|
|
731
|
+
if (found || complete)
|
|
732
|
+
settle(lookup, rows);
|
|
733
|
+
else
|
|
734
|
+
unresolved.push(lookup);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
catch {
|
|
738
|
+
unresolved = group; // the prefix read failing is not an answer; look directly
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
await mapWithConcurrency(unresolved, concurrency, async (lookup) => {
|
|
742
|
+
try {
|
|
743
|
+
const { task, duplicates } = await findTaskByExternalKey(lookup);
|
|
744
|
+
hits.set(externalKeyLookupId(lookup), { task, duplicates });
|
|
745
|
+
}
|
|
746
|
+
catch (err) {
|
|
747
|
+
hits.set(externalKeyLookupId(lookup), {
|
|
748
|
+
task: null,
|
|
749
|
+
duplicates: [],
|
|
750
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
return hits;
|
|
756
|
+
}
|
|
625
757
|
/**
|
|
626
758
|
* Which DEFAULTED assignees the create route will strip because they are not
|
|
627
759
|
* members of the task's space (`ASSIGNEE_NOT_IN_SPACE` degradation). Only a
|
package/dist/lib/triggers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EmbeddedActionRef, type TriggerPreflightReport, type TriggerSensorConfig } from '@skrr-ai/data-provider';
|
|
1
|
+
import { type EmbeddedActionRef, type TriggerIntentUnsupportedItem, type TriggerPreflightReport, type TriggerSensorConfig } from '@skrr-ai/data-provider';
|
|
2
2
|
export declare const TRIGGER_KINDS: readonly ["schedule", "once", "event", "stochastic", "self_scheduled", "on_demand", "form", "chat_message"];
|
|
3
3
|
export declare const TRIGGER_SCOPE_KINDS: readonly ["agent", "group_chat_subscriber", "space_oracle", "space_subscriber", "key_result_watcher", "task_watcher", "goal_watcher", "entity_reminder"];
|
|
4
4
|
/**
|
|
@@ -160,6 +160,13 @@ export interface TriggerParseIntentResponse {
|
|
|
160
160
|
message?: string;
|
|
161
161
|
needsClarification?: boolean;
|
|
162
162
|
proposedTrigger?: Record<string, unknown> | null;
|
|
163
|
+
/** Conditions the person asked for that the proposal does NOT apply (OSK-15402). */
|
|
164
|
+
unsupported?: TriggerIntentUnsupportedItem[];
|
|
165
|
+
/** The transcript including this turn; send it back with a reply to answer a question. */
|
|
166
|
+
history?: Array<{
|
|
167
|
+
role: 'user' | 'assistant';
|
|
168
|
+
content: string;
|
|
169
|
+
}>;
|
|
163
170
|
usage?: Record<string, unknown>;
|
|
164
171
|
}
|
|
165
172
|
export interface TriggerEventCatalogEntry {
|
|
@@ -225,6 +232,10 @@ export interface SpaceAssistantResponse {
|
|
|
225
232
|
export declare const SCOPE_IMPLIED_REF_KEY: Partial<Record<TriggerScopeKind, string>>;
|
|
226
233
|
export declare function parseKeyValueFlags(values: string[] | undefined, flagName: string, scopeKind?: string): Record<string, string>;
|
|
227
234
|
export declare function parseJsonObject(raw: string | undefined, flagName: string): Record<string, unknown> | undefined;
|
|
235
|
+
/**
|
|
236
|
+
* A JSON-array flag value: inline JSON, `@path` (a file), or a bare path to an existing file. A
|
|
237
|
+
* path used to reach `JSON.parse` and fail with "Unexpected token '/'" (OSK-15402).
|
|
238
|
+
*/
|
|
228
239
|
export declare function parseJsonArray(raw: string | undefined, flagName: string): unknown[] | undefined;
|
|
229
240
|
export declare function buildBuiltinActionPayload(input: {
|
|
230
241
|
builtinAction?: string;
|
|
@@ -288,6 +299,29 @@ export declare const FLAG_OWNER_KINDS: Readonly<Record<string, readonly string[]
|
|
|
288
299
|
export declare function assertKindFlagsApply(kind: unknown, flags: Record<string, unknown>, options?: {
|
|
289
300
|
kindIsFixed?: boolean;
|
|
290
301
|
}): void;
|
|
302
|
+
/**
|
|
303
|
+
* The request body path each `triggers create` flag writes, for reading a server refusal back
|
|
304
|
+
* in the person's own words (`lib/request-field-flags.ts`). The server validates the body and
|
|
305
|
+
* names `execution.workflowSignal`; the person typed `--workflow-event-key` (OSK-15313). A path
|
|
306
|
+
* missing here is simply left as the server wrote it.
|
|
307
|
+
*/
|
|
308
|
+
export declare const TRIGGER_CREATE_FIELD_FLAGS: Readonly<Record<string, string>>;
|
|
309
|
+
/**
|
|
310
|
+
* Flags an execution mode cannot work without, refused before the request is sent. The inverse
|
|
311
|
+
* case (`--workflow-outcome` without `--workflow-event-key`) was already refused by oclif's
|
|
312
|
+
* `dependsOn`; the missing key for `--mode workflow_signal` went to the server and came back
|
|
313
|
+
* naming an internal path (OSK-15313).
|
|
314
|
+
*/
|
|
315
|
+
export declare const TRIGGER_MODE_REQUIRED_FLAGS: Readonly<Record<string, ReadonlyArray<{
|
|
316
|
+
flag: string;
|
|
317
|
+
field: string;
|
|
318
|
+
why: string;
|
|
319
|
+
}>>>;
|
|
320
|
+
/**
|
|
321
|
+
* Refuse a mode whose required flag is missing, unless the body the person supplied
|
|
322
|
+
* (`--from-json`) already carries the field the flag would write.
|
|
323
|
+
*/
|
|
324
|
+
export declare function assertModeRequiredFlags(mode: unknown, flags: Record<string, unknown>, execution: Record<string, unknown> | undefined): void;
|
|
291
325
|
/**
|
|
292
326
|
* The sentence for a fire's `scheduledFire` report: what a MANUAL fire skipped that a scheduled
|
|
293
327
|
* one would not. A manual fire is exempt from the enabled gate, the working/quiet-hours overlay and
|
|
@@ -302,6 +336,13 @@ export declare function describeScheduledFire(result: Record<string, unknown>):
|
|
|
302
336
|
* about `in_progress` once). Only these are success for `triggers fire --strict`.
|
|
303
337
|
*/
|
|
304
338
|
export declare function fireWasAccepted(result: Record<string, unknown>): boolean;
|
|
339
|
+
/**
|
|
340
|
+
* Why `triggers fire --strict` failed. A strict failure used to print exactly what the exit-0 run
|
|
341
|
+
* printed and then exit 1, so nothing said what the flag had judged (OSK-16474). It names the
|
|
342
|
+
* outcome it refused, the reason, the server's sentence when there is one, and what --strict
|
|
343
|
+
* accepts.
|
|
344
|
+
*/
|
|
345
|
+
export declare function describeStrictFireRefusal(result: Record<string, unknown>): string;
|
|
305
346
|
/**
|
|
306
347
|
* The lines a fire or replay owes beyond `outcome=… reason=…` (OSK-15494): the sentence the
|
|
307
348
|
* server wrote ("Replayed fire … resolved a step that was not waiting when that fire ran:
|
package/dist/lib/triggers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.triggerApi = exports.AgentTriggerScopeError = exports.FLAG_OWNER_KINDS = exports.SCOPE_IMPLIED_REF_KEY = exports.TRIGGER_EVENT_CATALOG = exports.TRIGGER_EVENT_ENTITY_TYPES = exports.TRIGGER_CHAT_AUTH_MODES = exports.TRIGGER_CHAT_RESPONSE_MODES = exports.TRIGGER_CHAT_MODES = exports.TRIGGER_FORM_RESPONSE_MODES = exports.TRIGGER_CONCURRENCY_POLICIES = exports.TRIGGER_EVENT_FILTER_MODES = exports.TRIGGER_EXECUTOR_MODES = exports.TRIGGER_SCOPE_KINDS = exports.TRIGGER_KINDS = void 0;
|
|
3
|
+
exports.triggerApi = exports.AgentTriggerScopeError = exports.TRIGGER_MODE_REQUIRED_FLAGS = exports.TRIGGER_CREATE_FIELD_FLAGS = exports.FLAG_OWNER_KINDS = exports.SCOPE_IMPLIED_REF_KEY = exports.TRIGGER_EVENT_CATALOG = exports.TRIGGER_EVENT_ENTITY_TYPES = exports.TRIGGER_CHAT_AUTH_MODES = exports.TRIGGER_CHAT_RESPONSE_MODES = exports.TRIGGER_CHAT_MODES = exports.TRIGGER_FORM_RESPONSE_MODES = exports.TRIGGER_CONCURRENCY_POLICIES = exports.TRIGGER_EVENT_FILTER_MODES = exports.TRIGGER_EXECUTOR_MODES = exports.TRIGGER_SCOPE_KINDS = exports.TRIGGER_KINDS = void 0;
|
|
4
4
|
exports.validateEventReference = validateEventReference;
|
|
5
5
|
exports.validateEventFilter = validateEventFilter;
|
|
6
6
|
exports.serverEventCatalogIncludes = serverEventCatalogIncludes;
|
|
@@ -24,8 +24,10 @@ exports.setDefined = setDefined;
|
|
|
24
24
|
exports.mergeObjectField = mergeObjectField;
|
|
25
25
|
exports.mergeTriggerKindConfig = mergeTriggerKindConfig;
|
|
26
26
|
exports.assertKindFlagsApply = assertKindFlagsApply;
|
|
27
|
+
exports.assertModeRequiredFlags = assertModeRequiredFlags;
|
|
27
28
|
exports.describeScheduledFire = describeScheduledFire;
|
|
28
29
|
exports.fireWasAccepted = fireWasAccepted;
|
|
30
|
+
exports.describeStrictFireRefusal = describeStrictFireRefusal;
|
|
29
31
|
exports.describeFireDetail = describeFireDetail;
|
|
30
32
|
exports.formatFireInZone = formatFireInZone;
|
|
31
33
|
exports.nextFireLines = nextFireLines;
|
|
@@ -49,6 +51,7 @@ exports.renderTriggerSummary = renderTriggerSummary;
|
|
|
49
51
|
exports.renderLegacyRuns = renderLegacyRuns;
|
|
50
52
|
exports.renderFireAudit = renderFireAudit;
|
|
51
53
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
54
|
+
const node_fs_1 = require("node:fs");
|
|
52
55
|
const format_1 = require("./format");
|
|
53
56
|
exports.TRIGGER_KINDS = [
|
|
54
57
|
'schedule',
|
|
@@ -389,12 +392,28 @@ function parseJsonObject(raw, flagName) {
|
|
|
389
392
|
}
|
|
390
393
|
return parsed;
|
|
391
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* A JSON-array flag value: inline JSON, `@path` (a file), or a bare path to an existing file. A
|
|
397
|
+
* path used to reach `JSON.parse` and fail with "Unexpected token '/'" (OSK-15402).
|
|
398
|
+
*/
|
|
392
399
|
function parseJsonArray(raw, flagName) {
|
|
393
400
|
if (raw === undefined)
|
|
394
401
|
return undefined;
|
|
402
|
+
let text = raw;
|
|
403
|
+
const trimmed = raw.trim();
|
|
404
|
+
if (trimmed.startsWith('@') || (!/^[[{]/.test(trimmed) && (0, node_fs_1.existsSync)(trimmed))) {
|
|
405
|
+
const path = trimmed.startsWith('@') ? trimmed.slice(1) : trimmed;
|
|
406
|
+
try {
|
|
407
|
+
text = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
411
|
+
throw new Error(`Cannot read ${flagName} file ${path}: ${msg}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
395
414
|
let parsed;
|
|
396
415
|
try {
|
|
397
|
-
parsed = JSON.parse(
|
|
416
|
+
parsed = JSON.parse(text);
|
|
398
417
|
}
|
|
399
418
|
catch (err) {
|
|
400
419
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -442,6 +461,13 @@ function buildScheduleConfigFromFlags(flags) {
|
|
|
442
461
|
endAt: flags['end-at'],
|
|
443
462
|
staleCatchupMs: flags['stale-catchup-ms'],
|
|
444
463
|
});
|
|
464
|
+
// The range is the server's (`TRIGGER_STALE_CATCHUP_MS_RANGE`), checked here too so the refusal
|
|
465
|
+
// names the flag the person typed and happens before anything is sent (OSK-16474).
|
|
466
|
+
if (flags['stale-catchup-ms'] !== undefined) {
|
|
467
|
+
const refusal = (0, data_provider_1.describeStaleCatchupMsRefusal)(flags['stale-catchup-ms'], '--stale-catchup-ms');
|
|
468
|
+
if (refusal)
|
|
469
|
+
throw new Error(refusal);
|
|
470
|
+
}
|
|
445
471
|
return schedule;
|
|
446
472
|
}
|
|
447
473
|
function assertScheduleTimingShape(schedule, options = {}) {
|
|
@@ -746,6 +772,59 @@ function assertKindFlagsApply(kind, flags, options = {}) {
|
|
|
746
772
|
}
|
|
747
773
|
throw new Error(`${named.join(', ')} ${verb} not apply to ${subject}.${fix}`);
|
|
748
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* The request body path each `triggers create` flag writes, for reading a server refusal back
|
|
777
|
+
* in the person's own words (`lib/request-field-flags.ts`). The server validates the body and
|
|
778
|
+
* names `execution.workflowSignal`; the person typed `--workflow-event-key` (OSK-15313). A path
|
|
779
|
+
* missing here is simply left as the server wrote it.
|
|
780
|
+
*/
|
|
781
|
+
exports.TRIGGER_CREATE_FIELD_FLAGS = Object.freeze({
|
|
782
|
+
'execution.mode': '--mode',
|
|
783
|
+
'execution.promptTemplate': '--prompt',
|
|
784
|
+
'execution.conversationId': '--conversation',
|
|
785
|
+
'execution.builtinAction': '--builtin-action',
|
|
786
|
+
'execution.effectPolicy': '--effect-contract',
|
|
787
|
+
'execution.effectPolicy.contract': '--effect-contract',
|
|
788
|
+
'execution.effectPolicy.writeMode': '--effect-write-mode',
|
|
789
|
+
'execution.effectPolicy.options': '--effect-options',
|
|
790
|
+
// The whole block is written by --workflow-event-key: without it the block is not sent.
|
|
791
|
+
'execution.workflowSignal': '--workflow-event-key',
|
|
792
|
+
'execution.workflowSignal.eventKey': '--workflow-event-key',
|
|
793
|
+
'execution.workflowSignal.outcome': '--workflow-outcome',
|
|
794
|
+
'execution.dispatch': '--dispatch',
|
|
795
|
+
'execution.references': '--reference',
|
|
796
|
+
'execution.workingHours': '--working-hours',
|
|
797
|
+
'execution.quietHours': '--quiet-hours',
|
|
798
|
+
});
|
|
799
|
+
/**
|
|
800
|
+
* Flags an execution mode cannot work without, refused before the request is sent. The inverse
|
|
801
|
+
* case (`--workflow-outcome` without `--workflow-event-key`) was already refused by oclif's
|
|
802
|
+
* `dependsOn`; the missing key for `--mode workflow_signal` went to the server and came back
|
|
803
|
+
* naming an internal path (OSK-15313).
|
|
804
|
+
*/
|
|
805
|
+
exports.TRIGGER_MODE_REQUIRED_FLAGS = Object.freeze({
|
|
806
|
+
workflow_signal: [
|
|
807
|
+
{
|
|
808
|
+
flag: 'workflow-event-key',
|
|
809
|
+
field: 'workflowSignal',
|
|
810
|
+
why: 'the exact event key the parked workflow step waits on',
|
|
811
|
+
},
|
|
812
|
+
],
|
|
813
|
+
});
|
|
814
|
+
/**
|
|
815
|
+
* Refuse a mode whose required flag is missing, unless the body the person supplied
|
|
816
|
+
* (`--from-json`) already carries the field the flag would write.
|
|
817
|
+
*/
|
|
818
|
+
function assertModeRequiredFlags(mode, flags, execution) {
|
|
819
|
+
if (typeof mode !== 'string')
|
|
820
|
+
return;
|
|
821
|
+
const missing = (exports.TRIGGER_MODE_REQUIRED_FLAGS[mode] ?? []).filter((requirement) => !flagWasPassed(flags[requirement.flag]) && execution?.[requirement.field] === undefined);
|
|
822
|
+
if (missing.length === 0)
|
|
823
|
+
return;
|
|
824
|
+
throw new Error(missing
|
|
825
|
+
.map((requirement) => `--mode ${mode} requires --${requirement.flag}: ${requirement.why}.`)
|
|
826
|
+
.join(' '));
|
|
827
|
+
}
|
|
749
828
|
/**
|
|
750
829
|
* The sentence for a fire's `scheduledFire` report: what a MANUAL fire skipped that a scheduled
|
|
751
830
|
* one would not. A manual fire is exempt from the enabled gate, the working/quiet-hours overlay and
|
|
@@ -786,6 +865,23 @@ function fireWasAccepted(result) {
|
|
|
786
865
|
typeof result.originalOutcome === 'string' &&
|
|
787
866
|
accepted.includes(result.originalOutcome));
|
|
788
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Why `triggers fire --strict` failed. A strict failure used to print exactly what the exit-0 run
|
|
870
|
+
* printed and then exit 1, so nothing said what the flag had judged (OSK-16474). It names the
|
|
871
|
+
* outcome it refused, the reason, the server's sentence when there is one, and what --strict
|
|
872
|
+
* accepts.
|
|
873
|
+
*/
|
|
874
|
+
function describeStrictFireRefusal(result) {
|
|
875
|
+
const text = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
|
|
876
|
+
const outcome = text(result.outcome) || 'unknown';
|
|
877
|
+
const original = outcome === 'duplicate' ? text(result.originalOutcome) : '';
|
|
878
|
+
const reason = text(result.reason);
|
|
879
|
+
const sentence = text(result.detail) || text(result.errorMessage);
|
|
880
|
+
return (`--strict: the fire was not accepted (outcome ${outcome}` +
|
|
881
|
+
`${original ? `, a repeat of ${original}` : ''}${reason ? `, reason ${reason}` : ''})` +
|
|
882
|
+
`${sentence ? `: ${sentence}` : '.'} ` +
|
|
883
|
+
`--strict accepts only ${data_provider_1.ACCEPTED_AGENT_TRIGGER_FIRE_OUTCOMES.join(', ')}, or a repeat of one.`);
|
|
884
|
+
}
|
|
789
885
|
/**
|
|
790
886
|
* The lines a fire or replay owes beyond `outcome=… reason=…` (OSK-15494): the sentence the
|
|
791
887
|
* server wrote ("Replayed fire … resolved a step that was not waiting when that fire ran:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WorkflowIssue } from '@skrr-ai/data-provider';
|
|
2
|
+
/**
|
|
3
|
+
* Lines that say what in a workflow draft is wrong, and where.
|
|
4
|
+
*
|
|
5
|
+
* A draft may be STORED invalid — the canvas saves while you are still building
|
|
6
|
+
* — so `create` and `update` succeed on a graph `publish` will refuse. They used
|
|
7
|
+
* to print a bare success, and the author learned at publish (OSK-15328). The
|
|
8
|
+
* server now returns the shared linter's `issues` on every write and on
|
|
9
|
+
* validate; this renders them the same way on all three, each naming its node
|
|
10
|
+
* and field so it can be acted on without reading the graph again.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatWorkflowIssues(issues: WorkflowIssue[] | undefined): string[];
|
|
13
|
+
/** The heading plus issue lines for a draft write, or nothing when the draft is clean. */
|
|
14
|
+
export declare function describeDraftIssues(issues: WorkflowIssue[] | undefined): string[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatWorkflowIssues = formatWorkflowIssues;
|
|
4
|
+
exports.describeDraftIssues = describeDraftIssues;
|
|
5
|
+
/**
|
|
6
|
+
* Lines that say what in a workflow draft is wrong, and where.
|
|
7
|
+
*
|
|
8
|
+
* A draft may be STORED invalid — the canvas saves while you are still building
|
|
9
|
+
* — so `create` and `update` succeed on a graph `publish` will refuse. They used
|
|
10
|
+
* to print a bare success, and the author learned at publish (OSK-15328). The
|
|
11
|
+
* server now returns the shared linter's `issues` on every write and on
|
|
12
|
+
* validate; this renders them the same way on all three, each naming its node
|
|
13
|
+
* and field so it can be acted on without reading the graph again.
|
|
14
|
+
*/
|
|
15
|
+
function formatWorkflowIssues(issues) {
|
|
16
|
+
return (issues ?? []).map((issue) => {
|
|
17
|
+
const where = [issue.nodeKey, issue.path].filter(Boolean).join(' ');
|
|
18
|
+
const label = issue.severity === 'error' ? 'error' : 'warning';
|
|
19
|
+
return ` ${label}${where ? ` [${where}]` : ''}: ${issue.message}`;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** The heading plus issue lines for a draft write, or nothing when the draft is clean. */
|
|
23
|
+
function describeDraftIssues(issues) {
|
|
24
|
+
const list = issues ?? [];
|
|
25
|
+
if (!list.length)
|
|
26
|
+
return [];
|
|
27
|
+
const errors = list.filter((issue) => issue.severity === 'error').length;
|
|
28
|
+
const heading = errors
|
|
29
|
+
? `Saved, but this draft cannot be published yet (${errors} error${errors === 1 ? '' : 's'}):`
|
|
30
|
+
: `Saved. ${list.length} warning${list.length === 1 ? '' : 's'} (publishing is allowed):`;
|
|
31
|
+
return [heading, ...formatWorkflowIssues(list)];
|
|
32
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the plain-text workflow run view says about each node beyond its status.
|
|
3
|
+
*
|
|
4
|
+
* The table showed key/type/status/task, so three facts the server already knew were visible
|
|
5
|
+
* only under `--json` (OSK-15325): a loop that `succeeded` while one of its items failed, what a
|
|
6
|
+
* `waiting` node was waiting FOR (an event key, an approval, a child run), and how to resolve a
|
|
7
|
+
* wait a signal can resolve. Rows were also in node-creation order rather than graph order.
|
|
8
|
+
* Everything here reads server-derived fields (`depth`, `loopSummary`, `signalable`, `waitKind`)
|
|
9
|
+
* and recomputes none of them.
|
|
10
|
+
*/
|
|
11
|
+
export interface RunViewNode {
|
|
12
|
+
nodeKey?: unknown;
|
|
13
|
+
nodeType?: unknown;
|
|
14
|
+
status?: unknown;
|
|
15
|
+
depth?: unknown;
|
|
16
|
+
lane?: unknown;
|
|
17
|
+
attentionReason?: unknown;
|
|
18
|
+
error?: {
|
|
19
|
+
message?: unknown;
|
|
20
|
+
} | null;
|
|
21
|
+
waitKind?: unknown;
|
|
22
|
+
signalable?: unknown;
|
|
23
|
+
waitingFor?: unknown;
|
|
24
|
+
stoppedBecause?: {
|
|
25
|
+
code?: unknown;
|
|
26
|
+
message?: unknown;
|
|
27
|
+
} | null;
|
|
28
|
+
loopSummary?: {
|
|
29
|
+
total?: unknown;
|
|
30
|
+
completed?: unknown;
|
|
31
|
+
succeeded?: unknown;
|
|
32
|
+
failed?: unknown;
|
|
33
|
+
partial?: unknown;
|
|
34
|
+
settled?: unknown;
|
|
35
|
+
} | null;
|
|
36
|
+
}
|
|
37
|
+
/** One printable line: control characters (an ESC sequence included) become spaces. */
|
|
38
|
+
export declare function printable(value: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Nodes in graph order: the server's topological `depth` first, then the order the server
|
|
41
|
+
* returned them in (a stable sort), so siblings at one depth keep their relative order.
|
|
42
|
+
*/
|
|
43
|
+
export declare function orderNodesByGraph<T extends RunViewNode>(nodes: readonly T[]): T[];
|
|
44
|
+
/** "2 of 3 items succeeded, 1 failed" for a loop, or '' when the server sent no summary. */
|
|
45
|
+
export declare function describeLoopProgress(node: RunViewNode): string;
|
|
46
|
+
/** The DETAIL cell: what needs eyes, what a wait waits for, and how far a loop got. */
|
|
47
|
+
export declare function describeNodeDetail(node: RunViewNode, attention?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* How to resolve each wait a signal can resolve. Only the server's `signalable` decides: a loop
|
|
50
|
+
* or child-run wait is settled by its work, and offering a command for it would invite a person
|
|
51
|
+
* to skip that work.
|
|
52
|
+
*/
|
|
53
|
+
export declare function describeSignalHints(spaceId: string, runId: string, nodes: readonly RunViewNode[], bin?: string): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Lines a run's headline status cannot carry: a node that succeeded although some of its items
|
|
56
|
+
* failed. A loop under `onItemFailure: continue` with a permissive join succeeds with a failed
|
|
57
|
+
* item, so "run succeeded" alone hid the failure.
|
|
58
|
+
*/
|
|
59
|
+
export declare function describePartialOutcomes(nodes: readonly RunViewNode[]): string[];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printable = printable;
|
|
4
|
+
exports.orderNodesByGraph = orderNodesByGraph;
|
|
5
|
+
exports.describeLoopProgress = describeLoopProgress;
|
|
6
|
+
exports.describeNodeDetail = describeNodeDetail;
|
|
7
|
+
exports.describeSignalHints = describeSignalHints;
|
|
8
|
+
exports.describePartialOutcomes = describePartialOutcomes;
|
|
9
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
10
|
+
const format_1 = require("./format");
|
|
11
|
+
/** One printable line: control characters (an ESC sequence included) become spaces. */
|
|
12
|
+
function printable(value) {
|
|
13
|
+
// eslint-disable-next-line no-control-regex
|
|
14
|
+
return (0, format_1.collapseWhitespace)(value.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' '));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Nodes in graph order: the server's topological `depth` first, then the order the server
|
|
18
|
+
* returned them in (a stable sort), so siblings at one depth keep their relative order.
|
|
19
|
+
*/
|
|
20
|
+
function orderNodesByGraph(nodes) {
|
|
21
|
+
const depthOf = (node) => typeof node.depth === 'number' && Number.isFinite(node.depth)
|
|
22
|
+
? node.depth
|
|
23
|
+
: Number.MAX_SAFE_INTEGER;
|
|
24
|
+
return nodes
|
|
25
|
+
.map((node, index) => ({ node, index }))
|
|
26
|
+
.sort((a, b) => depthOf(a.node) - depthOf(b.node) || a.index - b.index)
|
|
27
|
+
.map((entry) => entry.node);
|
|
28
|
+
}
|
|
29
|
+
/** "2 of 3 items succeeded, 1 failed" for a loop, or '' when the server sent no summary. */
|
|
30
|
+
function describeLoopProgress(node) {
|
|
31
|
+
const summary = node.loopSummary;
|
|
32
|
+
if (node.nodeType !== 'loop' || !summary)
|
|
33
|
+
return '';
|
|
34
|
+
const total = Number(summary.total);
|
|
35
|
+
const completed = Number(summary.completed);
|
|
36
|
+
const succeeded = Number(summary.succeeded);
|
|
37
|
+
const failed = Number(summary.failed);
|
|
38
|
+
if (![total, completed, succeeded, failed].every(Number.isFinite))
|
|
39
|
+
return '';
|
|
40
|
+
const parts = [`${succeeded} of ${total} item${total === 1 ? '' : 's'} succeeded`];
|
|
41
|
+
if (failed > 0)
|
|
42
|
+
parts.push(`${failed} failed`);
|
|
43
|
+
if (summary.settled === true && completed < total)
|
|
44
|
+
parts.push(`${total - completed} not run`);
|
|
45
|
+
return parts.join(', ');
|
|
46
|
+
}
|
|
47
|
+
/** The DETAIL cell: what needs eyes, what a wait waits for, and how far a loop got. */
|
|
48
|
+
function describeNodeDetail(node, attention = '') {
|
|
49
|
+
const parts = [];
|
|
50
|
+
const wait = (0, data_provider_1.describeWorkflowNodeWait)(node);
|
|
51
|
+
// "approval; waiting for an approval" says one thing twice: the wait phrase carries it.
|
|
52
|
+
if (attention && attention !== wait?.kind)
|
|
53
|
+
parts.push(attention);
|
|
54
|
+
if (wait)
|
|
55
|
+
parts.push(wait.text);
|
|
56
|
+
// A cancelled node says why it stopped (the run's deadline, a person's stop), not the
|
|
57
|
+
// problem it had been parked on before (OSK-16473).
|
|
58
|
+
const stopped = node.status === 'cancelled' ? node.stoppedBecause?.message : undefined;
|
|
59
|
+
if (typeof stopped === 'string' && stopped)
|
|
60
|
+
parts.push(`stopped: ${stopped}`);
|
|
61
|
+
const loop = describeLoopProgress(node);
|
|
62
|
+
if (loop)
|
|
63
|
+
parts.push(loop);
|
|
64
|
+
return printable(parts.join('; '));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* How to resolve each wait a signal can resolve. Only the server's `signalable` decides: a loop
|
|
68
|
+
* or child-run wait is settled by its work, and offering a command for it would invite a person
|
|
69
|
+
* to skip that work.
|
|
70
|
+
*/
|
|
71
|
+
function describeSignalHints(spaceId, runId, nodes, bin = 'skrr') {
|
|
72
|
+
const lines = [];
|
|
73
|
+
for (const node of nodes) {
|
|
74
|
+
if (node.status !== 'waiting' || node.signalable !== true)
|
|
75
|
+
continue;
|
|
76
|
+
const key = printable(String(node.nodeKey ?? ''));
|
|
77
|
+
if (!key)
|
|
78
|
+
continue;
|
|
79
|
+
const wait = (0, data_provider_1.describeWorkflowNodeWait)(node);
|
|
80
|
+
const what = wait?.kind === 'approval' ? 'approve or reject it' : 'resolve it by hand';
|
|
81
|
+
lines.push(`To ${what}: ${bin} spaces workflows signal ${spaceId} ${runId} ${key}` +
|
|
82
|
+
(wait?.kind === 'approval'
|
|
83
|
+
? ' [--outcome failure]'
|
|
84
|
+
: ' [--outcome failure] [--payload <json>]'));
|
|
85
|
+
}
|
|
86
|
+
return lines;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Lines a run's headline status cannot carry: a node that succeeded although some of its items
|
|
90
|
+
* failed. A loop under `onItemFailure: continue` with a permissive join succeeds with a failed
|
|
91
|
+
* item, so "run succeeded" alone hid the failure.
|
|
92
|
+
*/
|
|
93
|
+
function describePartialOutcomes(nodes) {
|
|
94
|
+
const lines = [];
|
|
95
|
+
for (const node of nodes) {
|
|
96
|
+
const summary = node.loopSummary;
|
|
97
|
+
// A failed loop already says so in its STATUS column; only a success can hide one.
|
|
98
|
+
if (node.nodeType !== 'loop' || node.status !== 'succeeded')
|
|
99
|
+
continue;
|
|
100
|
+
if (!summary || summary.partial !== true)
|
|
101
|
+
continue;
|
|
102
|
+
const failed = Number(summary.failed);
|
|
103
|
+
const total = Number(summary.total);
|
|
104
|
+
if (!Number.isFinite(failed) || failed <= 0)
|
|
105
|
+
continue;
|
|
106
|
+
lines.push(`Note: ${printable(String(node.nodeKey ?? 'loop'))} succeeded, but ` +
|
|
107
|
+
`${failed} of ${Number.isFinite(total) ? total : '?'} item${total === 1 ? '' : 's'} failed.`);
|
|
108
|
+
}
|
|
109
|
+
return lines;
|
|
110
|
+
}
|