@skrr-ai/cli 0.1.108 → 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/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 +6718 -6370
- package/oclif.manifest.json +376 -52
- package/package.json +19 -1
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 {
|
|
@@ -288,6 +295,29 @@ export declare const FLAG_OWNER_KINDS: Readonly<Record<string, readonly string[]
|
|
|
288
295
|
export declare function assertKindFlagsApply(kind: unknown, flags: Record<string, unknown>, options?: {
|
|
289
296
|
kindIsFixed?: boolean;
|
|
290
297
|
}): void;
|
|
298
|
+
/**
|
|
299
|
+
* The request body path each `triggers create` flag writes, for reading a server refusal back
|
|
300
|
+
* in the person's own words (`lib/request-field-flags.ts`). The server validates the body and
|
|
301
|
+
* names `execution.workflowSignal`; the person typed `--workflow-event-key` (OSK-15313). A path
|
|
302
|
+
* missing here is simply left as the server wrote it.
|
|
303
|
+
*/
|
|
304
|
+
export declare const TRIGGER_CREATE_FIELD_FLAGS: Readonly<Record<string, string>>;
|
|
305
|
+
/**
|
|
306
|
+
* Flags an execution mode cannot work without, refused before the request is sent. The inverse
|
|
307
|
+
* case (`--workflow-outcome` without `--workflow-event-key`) was already refused by oclif's
|
|
308
|
+
* `dependsOn`; the missing key for `--mode workflow_signal` went to the server and came back
|
|
309
|
+
* naming an internal path (OSK-15313).
|
|
310
|
+
*/
|
|
311
|
+
export declare const TRIGGER_MODE_REQUIRED_FLAGS: Readonly<Record<string, ReadonlyArray<{
|
|
312
|
+
flag: string;
|
|
313
|
+
field: string;
|
|
314
|
+
why: string;
|
|
315
|
+
}>>>;
|
|
316
|
+
/**
|
|
317
|
+
* Refuse a mode whose required flag is missing, unless the body the person supplied
|
|
318
|
+
* (`--from-json`) already carries the field the flag would write.
|
|
319
|
+
*/
|
|
320
|
+
export declare function assertModeRequiredFlags(mode: unknown, flags: Record<string, unknown>, execution: Record<string, unknown> | undefined): void;
|
|
291
321
|
/**
|
|
292
322
|
* The sentence for a fire's `scheduledFire` report: what a MANUAL fire skipped that a scheduled
|
|
293
323
|
* one would not. A manual fire is exempt from the enabled gate, the working/quiet-hours overlay and
|
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,6 +24,7 @@ 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;
|
|
29
30
|
exports.describeFireDetail = describeFireDetail;
|
|
@@ -746,6 +747,59 @@ function assertKindFlagsApply(kind, flags, options = {}) {
|
|
|
746
747
|
}
|
|
747
748
|
throw new Error(`${named.join(', ')} ${verb} not apply to ${subject}.${fix}`);
|
|
748
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* The request body path each `triggers create` flag writes, for reading a server refusal back
|
|
752
|
+
* in the person's own words (`lib/request-field-flags.ts`). The server validates the body and
|
|
753
|
+
* names `execution.workflowSignal`; the person typed `--workflow-event-key` (OSK-15313). A path
|
|
754
|
+
* missing here is simply left as the server wrote it.
|
|
755
|
+
*/
|
|
756
|
+
exports.TRIGGER_CREATE_FIELD_FLAGS = Object.freeze({
|
|
757
|
+
'execution.mode': '--mode',
|
|
758
|
+
'execution.promptTemplate': '--prompt',
|
|
759
|
+
'execution.conversationId': '--conversation',
|
|
760
|
+
'execution.builtinAction': '--builtin-action',
|
|
761
|
+
'execution.effectPolicy': '--effect-contract',
|
|
762
|
+
'execution.effectPolicy.contract': '--effect-contract',
|
|
763
|
+
'execution.effectPolicy.writeMode': '--effect-write-mode',
|
|
764
|
+
'execution.effectPolicy.options': '--effect-options',
|
|
765
|
+
// The whole block is written by --workflow-event-key: without it the block is not sent.
|
|
766
|
+
'execution.workflowSignal': '--workflow-event-key',
|
|
767
|
+
'execution.workflowSignal.eventKey': '--workflow-event-key',
|
|
768
|
+
'execution.workflowSignal.outcome': '--workflow-outcome',
|
|
769
|
+
'execution.dispatch': '--dispatch',
|
|
770
|
+
'execution.references': '--reference',
|
|
771
|
+
'execution.workingHours': '--working-hours',
|
|
772
|
+
'execution.quietHours': '--quiet-hours',
|
|
773
|
+
});
|
|
774
|
+
/**
|
|
775
|
+
* Flags an execution mode cannot work without, refused before the request is sent. The inverse
|
|
776
|
+
* case (`--workflow-outcome` without `--workflow-event-key`) was already refused by oclif's
|
|
777
|
+
* `dependsOn`; the missing key for `--mode workflow_signal` went to the server and came back
|
|
778
|
+
* naming an internal path (OSK-15313).
|
|
779
|
+
*/
|
|
780
|
+
exports.TRIGGER_MODE_REQUIRED_FLAGS = Object.freeze({
|
|
781
|
+
workflow_signal: [
|
|
782
|
+
{
|
|
783
|
+
flag: 'workflow-event-key',
|
|
784
|
+
field: 'workflowSignal',
|
|
785
|
+
why: 'the exact event key the parked workflow step waits on',
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
});
|
|
789
|
+
/**
|
|
790
|
+
* Refuse a mode whose required flag is missing, unless the body the person supplied
|
|
791
|
+
* (`--from-json`) already carries the field the flag would write.
|
|
792
|
+
*/
|
|
793
|
+
function assertModeRequiredFlags(mode, flags, execution) {
|
|
794
|
+
if (typeof mode !== 'string')
|
|
795
|
+
return;
|
|
796
|
+
const missing = (exports.TRIGGER_MODE_REQUIRED_FLAGS[mode] ?? []).filter((requirement) => !flagWasPassed(flags[requirement.flag]) && execution?.[requirement.field] === undefined);
|
|
797
|
+
if (missing.length === 0)
|
|
798
|
+
return;
|
|
799
|
+
throw new Error(missing
|
|
800
|
+
.map((requirement) => `--mode ${mode} requires --${requirement.flag}: ${requirement.why}.`)
|
|
801
|
+
.join(' '));
|
|
802
|
+
}
|
|
749
803
|
/**
|
|
750
804
|
* The sentence for a fire's `scheduledFire` report: what a MANUAL fire skipped that a scheduled
|
|
751
805
|
* one would not. A manual fire is exempt from the enabled gate, the working/quiet-hours overlay and
|
|
@@ -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,55 @@
|
|
|
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
|
+
loopSummary?: {
|
|
25
|
+
total?: unknown;
|
|
26
|
+
completed?: unknown;
|
|
27
|
+
succeeded?: unknown;
|
|
28
|
+
failed?: unknown;
|
|
29
|
+
partial?: unknown;
|
|
30
|
+
settled?: unknown;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
/** One printable line: control characters (an ESC sequence included) become spaces. */
|
|
34
|
+
export declare function printable(value: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Nodes in graph order: the server's topological `depth` first, then the order the server
|
|
37
|
+
* returned them in (a stable sort), so siblings at one depth keep their relative order.
|
|
38
|
+
*/
|
|
39
|
+
export declare function orderNodesByGraph<T extends RunViewNode>(nodes: readonly T[]): T[];
|
|
40
|
+
/** "2 of 3 items succeeded, 1 failed" for a loop, or '' when the server sent no summary. */
|
|
41
|
+
export declare function describeLoopProgress(node: RunViewNode): string;
|
|
42
|
+
/** The DETAIL cell: what needs eyes, what a wait waits for, and how far a loop got. */
|
|
43
|
+
export declare function describeNodeDetail(node: RunViewNode, attention?: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* How to resolve each wait a signal can resolve. Only the server's `signalable` decides: a loop
|
|
46
|
+
* or child-run wait is settled by its work, and offering a command for it would invite a person
|
|
47
|
+
* to skip that work.
|
|
48
|
+
*/
|
|
49
|
+
export declare function describeSignalHints(spaceId: string, runId: string, nodes: readonly RunViewNode[], bin?: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Lines a run's headline status cannot carry: a node that succeeded although some of its items
|
|
52
|
+
* failed. A loop under `onItemFailure: continue` with a permissive join succeeds with a failed
|
|
53
|
+
* item, so "run succeeded" alone hid the failure.
|
|
54
|
+
*/
|
|
55
|
+
export declare function describePartialOutcomes(nodes: readonly RunViewNode[]): string[];
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
const loop = describeLoopProgress(node);
|
|
57
|
+
if (loop)
|
|
58
|
+
parts.push(loop);
|
|
59
|
+
return printable(parts.join('; '));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* How to resolve each wait a signal can resolve. Only the server's `signalable` decides: a loop
|
|
63
|
+
* or child-run wait is settled by its work, and offering a command for it would invite a person
|
|
64
|
+
* to skip that work.
|
|
65
|
+
*/
|
|
66
|
+
function describeSignalHints(spaceId, runId, nodes, bin = 'skrr') {
|
|
67
|
+
const lines = [];
|
|
68
|
+
for (const node of nodes) {
|
|
69
|
+
if (node.status !== 'waiting' || node.signalable !== true)
|
|
70
|
+
continue;
|
|
71
|
+
const key = printable(String(node.nodeKey ?? ''));
|
|
72
|
+
if (!key)
|
|
73
|
+
continue;
|
|
74
|
+
const wait = (0, data_provider_1.describeWorkflowNodeWait)(node);
|
|
75
|
+
const what = wait?.kind === 'approval' ? 'approve or reject it' : 'resolve it by hand';
|
|
76
|
+
lines.push(`To ${what}: ${bin} spaces workflows signal ${spaceId} ${runId} ${key}` +
|
|
77
|
+
(wait?.kind === 'approval'
|
|
78
|
+
? ' [--outcome failure]'
|
|
79
|
+
: ' [--outcome failure] [--payload <json>]'));
|
|
80
|
+
}
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Lines a run's headline status cannot carry: a node that succeeded although some of its items
|
|
85
|
+
* failed. A loop under `onItemFailure: continue` with a permissive join succeeds with a failed
|
|
86
|
+
* item, so "run succeeded" alone hid the failure.
|
|
87
|
+
*/
|
|
88
|
+
function describePartialOutcomes(nodes) {
|
|
89
|
+
const lines = [];
|
|
90
|
+
for (const node of nodes) {
|
|
91
|
+
const summary = node.loopSummary;
|
|
92
|
+
// A failed loop already says so in its STATUS column; only a success can hide one.
|
|
93
|
+
if (node.nodeType !== 'loop' || node.status !== 'succeeded')
|
|
94
|
+
continue;
|
|
95
|
+
if (!summary || summary.partial !== true)
|
|
96
|
+
continue;
|
|
97
|
+
const failed = Number(summary.failed);
|
|
98
|
+
const total = Number(summary.total);
|
|
99
|
+
if (!Number.isFinite(failed) || failed <= 0)
|
|
100
|
+
continue;
|
|
101
|
+
lines.push(`Note: ${printable(String(node.nodeKey ?? 'loop'))} succeeded, but ` +
|
|
102
|
+
`${failed} of ${Number.isFinite(total) ? total : '?'} item${total === 1 ? '' : 's'} failed.`);
|
|
103
|
+
}
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
package/dist/lib/workspaces.d.ts
CHANGED
|
@@ -12,11 +12,8 @@ export interface StewardshipCoverage {
|
|
|
12
12
|
truncated: boolean;
|
|
13
13
|
sources: StewardshipSource[];
|
|
14
14
|
}
|
|
15
|
-
export type WorkspaceGrantMetadata = Omit<GrantPublic, '
|
|
15
|
+
export type WorkspaceGrantMetadata = Omit<GrantPublic, 'createdAt' | 'updatedAt'> & {
|
|
16
16
|
ownerUserId: string;
|
|
17
|
-
credential: (NonNullable<GrantPublic['credential']> & {
|
|
18
|
-
fingerprint?: string;
|
|
19
|
-
}) | null;
|
|
20
17
|
createdAt?: string;
|
|
21
18
|
updatedAt?: string;
|
|
22
19
|
referencedBy: string[] | null;
|
|
@@ -47,6 +44,36 @@ export interface WorkspaceStewardshipReport extends WorkspaceGrantReport {
|
|
|
47
44
|
}> | null;
|
|
48
45
|
}
|
|
49
46
|
export declare function getWorkspaceStewardship(workspaceId: string, memberUserId: string): Promise<WorkspaceStewardshipReport>;
|
|
47
|
+
/** One current member of a workspace, as `GET /api/workspaces/:id/members` returns it. */
|
|
48
|
+
export interface WorkspaceMemberRow {
|
|
49
|
+
userId: string;
|
|
50
|
+
role: string;
|
|
51
|
+
joinedAt?: string;
|
|
52
|
+
user?: {
|
|
53
|
+
_id?: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
username?: string;
|
|
56
|
+
email?: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export declare function listWorkspaceMembers(workspaceId: string): Promise<WorkspaceMemberRow[]>;
|
|
60
|
+
/** A user id as the stewardship route takes it: a 24-hex ObjectId. */
|
|
61
|
+
export declare const USER_ID_PATTERN: RegExp;
|
|
62
|
+
export declare class MemberReferenceError extends Error {
|
|
63
|
+
constructor(message: string);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Turn what a person typed into the member's user id (OSK-15356).
|
|
67
|
+
*
|
|
68
|
+
* A 24-hex id is passed through untouched: the server answers for it, including
|
|
69
|
+
* for an id that is not (or is no longer) a member. An email is looked up among
|
|
70
|
+
* the workspace's CURRENT members, because that is the only directory this
|
|
71
|
+
* command may read; a former member's email therefore does not resolve, and the
|
|
72
|
+
* error says so instead of guessing. Anything else is refused here with the
|
|
73
|
+
* shapes that work, rather than sent to the server for a bare "valid user id
|
|
74
|
+
* is required".
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveMemberUserId(workspaceId: string, reference: string, list?: (workspaceId: string) => Promise<WorkspaceMemberRow[]>): Promise<string>;
|
|
50
77
|
export declare function listWorkspaceGrants(workspaceId: string): Promise<WorkspaceGrantReport>;
|
|
51
78
|
/**
|
|
52
79
|
* The workspaces this account belongs to.
|
package/dist/lib/workspaces.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemberReferenceError = exports.USER_ID_PATTERN = void 0;
|
|
3
4
|
exports.getWorkspaceStewardship = getWorkspaceStewardship;
|
|
5
|
+
exports.listWorkspaceMembers = listWorkspaceMembers;
|
|
6
|
+
exports.resolveMemberUserId = resolveMemberUserId;
|
|
4
7
|
exports.listWorkspaceGrants = listWorkspaceGrants;
|
|
5
8
|
exports.describeUnselectedWorkspace = describeUnselectedWorkspace;
|
|
6
9
|
exports.listWorkspaces = listWorkspaces;
|
|
@@ -15,6 +18,46 @@ const triggers_1 = require("./triggers");
|
|
|
15
18
|
async function getWorkspaceStewardship(workspaceId, memberUserId) {
|
|
16
19
|
return (0, api_fetch_1.apiFetch)(`/api/workspaces/${encodeURIComponent(workspaceId)}/stewardship/members/${encodeURIComponent(memberUserId)}`);
|
|
17
20
|
}
|
|
21
|
+
async function listWorkspaceMembers(workspaceId) {
|
|
22
|
+
const response = await (0, api_fetch_1.apiFetch)(`/api/workspaces/${encodeURIComponent(workspaceId)}/members`);
|
|
23
|
+
return Array.isArray(response?.data) ? response.data : [];
|
|
24
|
+
}
|
|
25
|
+
/** A user id as the stewardship route takes it: a 24-hex ObjectId. */
|
|
26
|
+
exports.USER_ID_PATTERN = /^[a-f0-9]{24}$/i;
|
|
27
|
+
class MemberReferenceError extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'MemberReferenceError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.MemberReferenceError = MemberReferenceError;
|
|
34
|
+
/**
|
|
35
|
+
* Turn what a person typed into the member's user id (OSK-15356).
|
|
36
|
+
*
|
|
37
|
+
* A 24-hex id is passed through untouched: the server answers for it, including
|
|
38
|
+
* for an id that is not (or is no longer) a member. An email is looked up among
|
|
39
|
+
* the workspace's CURRENT members, because that is the only directory this
|
|
40
|
+
* command may read; a former member's email therefore does not resolve, and the
|
|
41
|
+
* error says so instead of guessing. Anything else is refused here with the
|
|
42
|
+
* shapes that work, rather than sent to the server for a bare "valid user id
|
|
43
|
+
* is required".
|
|
44
|
+
*/
|
|
45
|
+
async function resolveMemberUserId(workspaceId, reference, list = listWorkspaceMembers) {
|
|
46
|
+
const value = reference.trim();
|
|
47
|
+
if (exports.USER_ID_PATTERN.test(value))
|
|
48
|
+
return value;
|
|
49
|
+
const listHint = `\`skrr workspaces members ${workspaceId}\` lists current members and their user ids.`;
|
|
50
|
+
if (!value.includes('@')) {
|
|
51
|
+
throw new MemberReferenceError(`"${reference}" is not a user id or an email. A user id is 24 hexadecimal characters. ${listHint}`);
|
|
52
|
+
}
|
|
53
|
+
const email = value.toLowerCase();
|
|
54
|
+
const matches = (await list(workspaceId)).filter((member) => member.user?.email?.toLowerCase() === email);
|
|
55
|
+
if (matches.length === 1)
|
|
56
|
+
return String(matches[0].userId);
|
|
57
|
+
throw new MemberReferenceError(matches.length === 0
|
|
58
|
+
? `No current member of this workspace has the email ${value}. A former member's email cannot be looked up here; use their user id. ${listHint}`
|
|
59
|
+
: `More than one member has the email ${value}; use the user id. ${listHint}`);
|
|
60
|
+
}
|
|
18
61
|
async function listWorkspaceGrants(workspaceId) {
|
|
19
62
|
return (0, api_fetch_1.apiFetch)(`/api/workspaces/${encodeURIComponent(workspaceId)}/stewardship/grants`);
|
|
20
63
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type WriteTarget = {
|
|
2
|
+
/** The API origin writes go to, e.g. `https://skrr.ai`. */
|
|
3
|
+
server: string;
|
|
4
|
+
/** The signed-in account's email, or null when it could not be read. */
|
|
5
|
+
account: string | null;
|
|
6
|
+
accountId: string | null;
|
|
7
|
+
/** The workspace the request is scoped to, or null for none. */
|
|
8
|
+
workspace: string | null;
|
|
9
|
+
};
|
|
10
|
+
/** `stevedu518@gmail.com on https://skrr.ai (no workspace)` */
|
|
11
|
+
export declare function formatWriteTarget(target: WriteTarget): string;
|
|
12
|
+
/**
|
|
13
|
+
* Does one `--expect-target` value describe this target?
|
|
14
|
+
*
|
|
15
|
+
* - contains `@` -> the account email (case-insensitive)
|
|
16
|
+
* - a URL or a host name -> the server's host (`skrr.ai` matches `https://skrr.ai`)
|
|
17
|
+
* - anything else -> the account id
|
|
18
|
+
*
|
|
19
|
+
* One value names one fact; pass the flag twice to pin both account and server.
|
|
20
|
+
* An unreadable fact never matches: an expectation about an account the command
|
|
21
|
+
* could not read is not met.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchesExpectedTarget(target: WriteTarget, expected: string): boolean;
|
|
24
|
+
/** The first expectation that does NOT hold, or undefined when all do. */
|
|
25
|
+
export declare function firstUnmetExpectation(target: WriteTarget, expected: readonly string[] | undefined): string | undefined;
|
|
26
|
+
/** The assertion flag, declared once so every bulk writer spells it the same way. */
|
|
27
|
+
export declare const expectTargetFlag: {
|
|
28
|
+
'expect-target': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
29
|
+
};
|