@cortexkit/antigravity-auth-core 2.2.1 → 3.0.1
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 +24 -2
- package/dist/abort-forwarding.d.ts +3 -0
- package/dist/abort-forwarding.d.ts.map +1 -0
- package/dist/abort-forwarding.js +19 -0
- package/dist/abort-forwarding.js.map +1 -0
- package/dist/account-identity.d.ts +52 -0
- package/dist/account-identity.d.ts.map +1 -0
- package/dist/account-identity.js +66 -0
- package/dist/account-identity.js.map +1 -0
- package/dist/account-manager.d.ts +228 -43
- package/dist/account-manager.d.ts.map +1 -1
- package/dist/account-manager.js +942 -903
- package/dist/account-manager.js.map +1 -1
- package/dist/account-migration.d.ts +309 -0
- package/dist/account-migration.d.ts.map +1 -0
- package/dist/account-migration.js +2284 -0
- package/dist/account-migration.js.map +1 -0
- package/dist/account-repository-codecs.d.ts +82 -0
- package/dist/account-repository-codecs.d.ts.map +1 -0
- package/dist/account-repository-codecs.js +609 -0
- package/dist/account-repository-codecs.js.map +1 -0
- package/dist/account-repository-types.d.ts +831 -0
- package/dist/account-repository-types.d.ts.map +1 -0
- package/dist/account-repository-types.js +137 -0
- package/dist/account-repository-types.js.map +1 -0
- package/dist/account-repository.d.ts +442 -0
- package/dist/account-repository.d.ts.map +1 -0
- package/dist/account-repository.js +2253 -0
- package/dist/account-repository.js.map +1 -0
- package/dist/account-selector.d.ts +470 -0
- package/dist/account-selector.d.ts.map +1 -0
- package/dist/account-selector.js +1383 -0
- package/dist/account-selector.js.map +1 -0
- package/dist/account-storage.d.ts.map +1 -1
- package/dist/account-storage.js +5 -0
- package/dist/account-storage.js.map +1 -1
- package/dist/agy-request-metadata.d.ts.map +1 -1
- package/dist/agy-request-metadata.js +8 -21
- package/dist/agy-request-metadata.js.map +1 -1
- package/dist/agy-transport.d.ts.map +1 -1
- package/dist/agy-transport.js +21 -35
- package/dist/agy-transport.js.map +1 -1
- package/dist/antigravity-command-menu.d.ts +171 -0
- package/dist/antigravity-command-menu.d.ts.map +1 -0
- package/dist/antigravity-command-menu.js +523 -0
- package/dist/antigravity-command-menu.js.map +1 -0
- package/dist/atomic-write.d.ts +18 -1
- package/dist/atomic-write.d.ts.map +1 -1
- package/dist/atomic-write.js +15 -3
- package/dist/atomic-write.js.map +1 -1
- package/dist/common-auth-embedded/NOTICE.txt +21 -0
- package/dist/common-auth-embedded/auth-menu/accounts.d.ts +97 -0
- package/dist/common-auth-embedded/auth-menu/accounts.js +387 -0
- package/dist/common-auth-embedded/auth-menu/ansi.d.ts +26 -0
- package/dist/common-auth-embedded/auth-menu/ansi.js +98 -0
- package/dist/common-auth-embedded/auth-menu/confirm.d.ts +7 -0
- package/dist/common-auth-embedded/auth-menu/confirm.js +21 -0
- package/dist/common-auth-embedded/auth-menu/doctor.d.ts +52 -0
- package/dist/common-auth-embedded/auth-menu/doctor.js +81 -0
- package/dist/common-auth-embedded/auth-menu/index.d.ts +17 -0
- package/dist/common-auth-embedded/auth-menu/index.js +12 -0
- package/dist/common-auth-embedded/auth-menu/login.d.ts +55 -0
- package/dist/common-auth-embedded/auth-menu/login.js +83 -0
- package/dist/common-auth-embedded/auth-menu/menu.d.ts +59 -0
- package/dist/common-auth-embedded/auth-menu/menu.js +73 -0
- package/dist/common-auth-embedded/auth-menu/opencode-v1.d.ts +49 -0
- package/dist/common-auth-embedded/auth-menu/opencode-v1.js +47 -0
- package/dist/common-auth-embedded/auth-menu/select.d.ts +21 -0
- package/dist/common-auth-embedded/auth-menu/select.js +159 -0
- package/dist/common-auth-embedded/auth-menu/terminal.d.ts +43 -0
- package/dist/common-auth-embedded/auth-menu/terminal.js +19 -0
- package/dist/common-auth-embedded/claustrum/consumer.d.ts +102 -0
- package/dist/common-auth-embedded/claustrum/consumer.js +298 -0
- package/dist/common-auth-embedded/claustrum/custody.d.ts +175 -0
- package/dist/common-auth-embedded/claustrum/custody.js +354 -0
- package/dist/common-auth-embedded/claustrum/enrollment.d.ts +158 -0
- package/dist/common-auth-embedded/claustrum/enrollment.js +759 -0
- package/dist/common-auth-embedded/claustrum/errors.d.ts +16 -0
- package/dist/common-auth-embedded/claustrum/errors.js +8 -0
- package/dist/common-auth-embedded/claustrum/host-slot.d.ts +39 -0
- package/dist/common-auth-embedded/claustrum/host-slot.js +72 -0
- package/dist/common-auth-embedded/claustrum/index.d.ts +18 -0
- package/dist/common-auth-embedded/claustrum/index.js +22 -0
- package/dist/common-auth-embedded/claustrum/interlock.d.ts +27 -0
- package/dist/common-auth-embedded/claustrum/interlock.js +29 -0
- package/dist/common-auth-embedded/claustrum/roster.d.ts +208 -0
- package/dist/common-auth-embedded/claustrum/roster.js +536 -0
- package/dist/common-auth-embedded/commands/builtins.d.ts +71 -0
- package/dist/common-auth-embedded/commands/builtins.js +534 -0
- package/dist/common-auth-embedded/commands/index.d.ts +10 -0
- package/dist/common-auth-embedded/commands/index.js +5 -0
- package/dist/common-auth-embedded/commands/menu.d.ts +61 -0
- package/dist/common-auth-embedded/commands/menu.js +300 -0
- package/dist/common-auth-embedded/commands/model.d.ts +238 -0
- package/dist/common-auth-embedded/commands/model.js +16 -0
- package/dist/common-auth-embedded/commands/pi.d.ts +23 -0
- package/dist/common-auth-embedded/commands/pi.js +206 -0
- package/dist/common-auth-embedded/commands/seam.d.ts +72 -0
- package/dist/common-auth-embedded/commands/seam.js +301 -0
- package/dist/common-auth-embedded/fs/atomic-write.d.ts +20 -0
- package/dist/common-auth-embedded/fs/atomic-write.js +80 -0
- package/dist/common-auth-embedded/fs/index.d.ts +7 -0
- package/dist/common-auth-embedded/fs/index.js +6 -0
- package/dist/common-auth-embedded/fs/lock-constants.d.ts +14 -0
- package/dist/common-auth-embedded/fs/lock-constants.js +14 -0
- package/dist/common-auth-embedded/fs/refresh-file-lock.d.ts +33 -0
- package/dist/common-auth-embedded/fs/refresh-file-lock.js +501 -0
- package/dist/common-auth-embedded/fs/with-lock.d.ts +33 -0
- package/dist/common-auth-embedded/fs/with-lock.js +48 -0
- package/dist/common-auth-embedded/logger/capture-sink.d.ts +14 -0
- package/dist/common-auth-embedded/logger/capture-sink.js +13 -0
- package/dist/common-auth-embedded/logger/engine.d.ts +100 -0
- package/dist/common-auth-embedded/logger/engine.js +246 -0
- package/dist/common-auth-embedded/logger/index.d.ts +6 -0
- package/dist/common-auth-embedded/logger/index.js +3 -0
- package/dist/common-auth-embedded/logger/redact.d.ts +11 -0
- package/dist/common-auth-embedded/logger/redact.js +83 -0
- package/dist/common-auth-embedded/pi-slot/index.d.ts +3 -0
- package/dist/common-auth-embedded/pi-slot/index.js +2 -0
- package/dist/common-auth-embedded/pi-slot/lock.js +212 -0
- package/dist/common-auth-embedded/pi-slot/slot.d.ts +49 -0
- package/dist/common-auth-embedded/pi-slot/slot.js +383 -0
- package/dist/common-auth-embedded/quota/codec.d.ts +11 -0
- package/dist/common-auth-embedded/quota/codec.js +12 -0
- package/dist/common-auth-embedded/quota/format.d.ts +46 -0
- package/dist/common-auth-embedded/quota/format.js +138 -0
- package/dist/common-auth-embedded/quota/index.d.ts +10 -0
- package/dist/common-auth-embedded/quota/index.js +5 -0
- package/dist/common-auth-embedded/quota/map.d.ts +63 -0
- package/dist/common-auth-embedded/quota/map.js +105 -0
- package/dist/common-auth-embedded/quota/merge.d.ts +48 -0
- package/dist/common-auth-embedded/quota/merge.js +162 -0
- package/dist/common-auth-embedded/quota/projection.d.ts +64 -0
- package/dist/common-auth-embedded/quota/projection.js +134 -0
- package/dist/common-auth-embedded/routing/admission.d.ts +93 -0
- package/dist/common-auth-embedded/routing/admission.js +142 -0
- package/dist/common-auth-embedded/routing/index.d.ts +8 -0
- package/dist/common-auth-embedded/routing/index.js +4 -0
- package/dist/common-auth-embedded/routing/ordered.d.ts +47 -0
- package/dist/common-auth-embedded/routing/ordered.js +58 -0
- package/dist/common-auth-embedded/routing/pins.d.ts +22 -0
- package/dist/common-auth-embedded/routing/pins.js +34 -0
- package/dist/common-auth-embedded/routing/sticky.d.ts +158 -0
- package/dist/common-auth-embedded/routing/sticky.js +339 -0
- package/dist/common-auth-embedded/source-output.json +1090 -0
- package/dist/common-auth-embedded/store/attribution.d.ts +28 -0
- package/dist/common-auth-embedded/store/attribution.js +60 -0
- package/dist/common-auth-embedded/store/errors.d.ts +55 -0
- package/dist/common-auth-embedded/store/errors.js +38 -0
- package/dist/common-auth-embedded/store/hooks.d.ts +12 -0
- package/dist/common-auth-embedded/store/hooks.js +34 -0
- package/dist/common-auth-embedded/store/identity.d.ts +39 -0
- package/dist/common-auth-embedded/store/identity.js +79 -0
- package/dist/common-auth-embedded/store/index.d.ts +21 -0
- package/dist/common-auth-embedded/store/index.js +7 -0
- package/dist/common-auth-embedded/store/mutate.d.ts +189 -0
- package/dist/common-auth-embedded/store/mutate.js +500 -0
- package/dist/common-auth-embedded/store/pool.d.ts +196 -0
- package/dist/common-auth-embedded/store/pool.js +120 -0
- package/dist/common-auth-embedded/store/provider-state.d.ts +131 -0
- package/dist/common-auth-embedded/store/provider-state.js +226 -0
- package/dist/common-auth-embedded/store/publication.d.ts +47 -0
- package/dist/common-auth-embedded/store/publication.js +304 -0
- package/dist/common-auth-embedded/store/pull.d.ts +36 -0
- package/dist/common-auth-embedded/store/pull.js +143 -0
- package/dist/common-auth-embedded/store/refresh-lock.d.ts +63 -0
- package/dist/common-auth-embedded/store/refresh-lock.js +136 -0
- package/dist/common-auth-embedded/store/refresh.d.ts +72 -0
- package/dist/common-auth-embedded/store/refresh.js +225 -0
- package/dist/common-auth-embedded/store/reserved.js +13 -0
- package/dist/common-auth-embedded/store/rows.d.ts +313 -0
- package/dist/common-auth-embedded/store/rows.js +1258 -0
- package/dist/common-auth-embedded/store/runtime.d.ts +36 -0
- package/dist/common-auth-embedded/store/runtime.js +68 -0
- package/dist/common-auth-embedded/store/schema.d.ts +443 -0
- package/dist/common-auth-embedded/store/schema.js +754 -0
- package/dist/common-auth-embedded/store/settings.d.ts +63 -0
- package/dist/common-auth-embedded/store/settings.js +120 -0
- package/dist/common-auth-embedded/store/torn.d.ts +78 -0
- package/dist/common-auth-embedded/store/torn.js +294 -0
- package/dist/common-auth-embedded/util/any-signal.js +42 -0
- package/dist/common-auth-runtime.d.ts +73 -0
- package/dist/common-auth-runtime.d.ts.map +1 -0
- package/dist/common-auth-runtime.js +65 -0
- package/dist/common-auth-runtime.js.map +1 -0
- package/dist/credential-redaction.d.ts +13 -0
- package/dist/credential-redaction.d.ts.map +1 -0
- package/dist/credential-redaction.js +230 -0
- package/dist/credential-redaction.js.map +1 -0
- package/dist/fetch-timeout.d.ts +4 -2
- package/dist/fetch-timeout.d.ts.map +1 -1
- package/dist/fetch-timeout.js +121 -14
- package/dist/fetch-timeout.js.map +1 -1
- package/dist/fingerprint.d.ts +3 -3
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +4 -4
- package/dist/fingerprint.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/model-registry.d.ts +16 -0
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.js +242 -30
- package/dist/model-registry.js.map +1 -1
- package/dist/quota-manager.d.ts.map +1 -1
- package/dist/quota-manager.js +35 -10
- package/dist/quota-manager.js.map +1 -1
- package/dist/quota-presentation.d.ts +10 -0
- package/dist/quota-presentation.d.ts.map +1 -0
- package/dist/quota-presentation.js +60 -0
- package/dist/quota-presentation.js.map +1 -0
- package/dist/quota-types.d.ts +2 -0
- package/dist/quota-types.d.ts.map +1 -1
- package/dist/rotation.d.ts +8 -0
- package/dist/rotation.d.ts.map +1 -1
- package/dist/rotation.js +24 -0
- package/dist/rotation.js.map +1 -1
- package/dist/transform/agy-generation.d.ts +3 -0
- package/dist/transform/agy-generation.d.ts.map +1 -0
- package/dist/transform/agy-generation.js +28 -0
- package/dist/transform/agy-generation.js.map +1 -0
- package/dist/transform/claude.d.ts +7 -3
- package/dist/transform/claude.d.ts.map +1 -1
- package/dist/transform/claude.js +8 -2
- package/dist/transform/claude.js.map +1 -1
- package/dist/transform/index.d.ts +1 -0
- package/dist/transform/index.d.ts.map +1 -1
- package/dist/transform/index.js +1 -0
- package/dist/transform/index.js.map +1 -1
- package/dist/transform/model-resolver.d.ts +2 -1
- package/dist/transform/model-resolver.d.ts.map +1 -1
- package/dist/transform/model-resolver.js +46 -5
- package/dist/transform/model-resolver.js.map +1 -1
- package/dist/transform/types.d.ts +1 -1
- package/dist/transform/types.d.ts.map +1 -1
- package/dist/vault-account-source.d.ts +183 -0
- package/dist/vault-account-source.d.ts.map +1 -0
- package/dist/vault-account-source.js +296 -0
- package/dist/vault-account-source.js.map +1 -0
- package/dist/vault-enrollment-authority.d.ts +16 -0
- package/dist/vault-enrollment-authority.d.ts.map +1 -0
- package/dist/vault-enrollment-authority.js +19 -0
- package/dist/vault-enrollment-authority.js.map +1 -0
- package/dist/vault-family.d.ts +11 -0
- package/dist/vault-family.d.ts.map +1 -0
- package/dist/vault-family.js +24 -0
- package/dist/vault-family.js.map +1 -0
- package/dist/vault-provider-state.d.ts +162 -0
- package/dist/vault-provider-state.d.ts.map +1 -0
- package/dist/vault-provider-state.js +422 -0
- package/dist/vault-provider-state.js.map +1 -0
- package/package.json +39 -2
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// One slash command per plugin: the menu a plugin creates once and serves to
|
|
2
|
+
// every invocation. It keeps no per-invocation state; each `open` and `apply`
|
|
3
|
+
// builds its sections afresh from the store and gets its own copy of the
|
|
4
|
+
// caller's context.
|
|
5
|
+
import { createLogger } from '../logger/index.js';
|
|
6
|
+
import { builtinSections, } from './builtins.js';
|
|
7
|
+
import { applyResult, confirmationOf, createTextRedactor, dialogPayload, projectFailure, } from './seam.js';
|
|
8
|
+
const BUILTIN_IDS = new Set([
|
|
9
|
+
'accounts',
|
|
10
|
+
'quota',
|
|
11
|
+
'routing',
|
|
12
|
+
'limits',
|
|
13
|
+
'cache',
|
|
14
|
+
'diagnostics',
|
|
15
|
+
]);
|
|
16
|
+
const STORE_SLOTS = [
|
|
17
|
+
'accounts',
|
|
18
|
+
'quota',
|
|
19
|
+
'routing',
|
|
20
|
+
'limits',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* A private copy of the caller's context, taken before the first await. Work
|
|
24
|
+
* an invocation leaves running reports through this copy, so a host that
|
|
25
|
+
* reuses and rebinds one context object for the next session cannot pull an
|
|
26
|
+
* earlier invocation's feedback over to it.
|
|
27
|
+
*/
|
|
28
|
+
function ownInvocation(invocation, redact) {
|
|
29
|
+
const { sessionId } = invocation;
|
|
30
|
+
const notify = invocation.notify.bind(invocation);
|
|
31
|
+
// A notification leaves the process just as a payload does, so its text
|
|
32
|
+
// goes through the same redactor.
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
35
|
+
notify: (message, kind) => kind === undefined
|
|
36
|
+
? notify(redact(String(message)))
|
|
37
|
+
: notify(redact(String(message)), kind),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function coerceOne(knob, raw) {
|
|
41
|
+
switch (knob.kind) {
|
|
42
|
+
case 'choice': {
|
|
43
|
+
const value = raw ?? knob.value;
|
|
44
|
+
if (typeof value === 'string' &&
|
|
45
|
+
knob.choices.some((choice) => choice.value === value))
|
|
46
|
+
return { value };
|
|
47
|
+
return { problem: `Choose one of the options for ${knob.label}.` };
|
|
48
|
+
}
|
|
49
|
+
case 'toggle': {
|
|
50
|
+
const value = raw ?? knob.value;
|
|
51
|
+
return typeof value === 'boolean'
|
|
52
|
+
? { value }
|
|
53
|
+
: { problem: `${knob.label} must be on or off.` };
|
|
54
|
+
}
|
|
55
|
+
case 'number': {
|
|
56
|
+
const value = raw === undefined ? (knob.value ?? null) : raw;
|
|
57
|
+
if (value === null || value === '') {
|
|
58
|
+
return knob.required
|
|
59
|
+
? { problem: `${knob.label} needs a number.` }
|
|
60
|
+
: { value: null };
|
|
61
|
+
}
|
|
62
|
+
const number = typeof value === 'number' ? value : Number(value);
|
|
63
|
+
if (typeof value === 'boolean' || !Number.isFinite(number))
|
|
64
|
+
return { problem: `${knob.label} needs a number.` };
|
|
65
|
+
if (knob.min !== undefined && number < knob.min)
|
|
66
|
+
return { problem: `${knob.label} must be at least ${knob.min}.` };
|
|
67
|
+
if (knob.max !== undefined && number > knob.max)
|
|
68
|
+
return { problem: `${knob.label} must be at most ${knob.max}.` };
|
|
69
|
+
return { value: number };
|
|
70
|
+
}
|
|
71
|
+
case 'text': {
|
|
72
|
+
const value = raw === undefined ? (knob.value ?? null) : raw;
|
|
73
|
+
if (value === null || value === '')
|
|
74
|
+
return knob.required
|
|
75
|
+
? { problem: `${knob.label} cannot be empty.` }
|
|
76
|
+
: { value: null };
|
|
77
|
+
return typeof value === 'string'
|
|
78
|
+
? { value }
|
|
79
|
+
: { problem: `${knob.label} must be text.` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** The action's inputs, checked against its knobs; unknown names are ignored. */
|
|
84
|
+
function coerceValues(knobs, raw) {
|
|
85
|
+
const values = {};
|
|
86
|
+
for (const knob of knobs) {
|
|
87
|
+
const given = raw && Object.hasOwn(raw, knob.id) ? raw[knob.id] : undefined;
|
|
88
|
+
const result = coerceOne(knob, given);
|
|
89
|
+
if ('problem' in result)
|
|
90
|
+
return result;
|
|
91
|
+
values[knob.id] = result.value;
|
|
92
|
+
}
|
|
93
|
+
return { values };
|
|
94
|
+
}
|
|
95
|
+
function findAction(sections, request) {
|
|
96
|
+
const section = sections.find((entry) => entry.id === request.sectionId);
|
|
97
|
+
if (!section)
|
|
98
|
+
return undefined;
|
|
99
|
+
const actions = request.itemId === undefined
|
|
100
|
+
? section.content.actions
|
|
101
|
+
: section.content.items?.find((item) => item.id === request.itemId)
|
|
102
|
+
?.actions;
|
|
103
|
+
return actions?.find((action) => action.id === request.actionId);
|
|
104
|
+
}
|
|
105
|
+
export function createCommandMenu(options) {
|
|
106
|
+
const logger = options.logger ?? createLogger('commands');
|
|
107
|
+
const redact = createTextRedactor(options.redaction);
|
|
108
|
+
const seam = { logger, redact };
|
|
109
|
+
const now = options.now ?? Date.now;
|
|
110
|
+
const extras = options.extras ?? [];
|
|
111
|
+
const replaced = options.replace ?? {};
|
|
112
|
+
const builtSlots = STORE_SLOTS.filter((slot) => !replaced[slot]);
|
|
113
|
+
for (const slot of STORE_SLOTS) {
|
|
114
|
+
if (replaced[slot] && options[slot] !== undefined)
|
|
115
|
+
throw new Error(`the ${slot} section is replaced; its built-in options cannot also be given`);
|
|
116
|
+
}
|
|
117
|
+
const store = options.store;
|
|
118
|
+
if (builtSlots.length > 0 && !store)
|
|
119
|
+
throw new Error(`a store is required for the built-in ${builtSlots.join(', ')} section${builtSlots.length > 1 ? 's' : ''}`);
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
for (const extra of extras) {
|
|
122
|
+
if (BUILTIN_IDS.has(extra.id) || seen.has(extra.id))
|
|
123
|
+
throw new Error(`extra section id ${extra.id} is taken`);
|
|
124
|
+
seen.add(extra.id);
|
|
125
|
+
}
|
|
126
|
+
async function plugin(id, slot, section, invocation) {
|
|
127
|
+
try {
|
|
128
|
+
return {
|
|
129
|
+
id,
|
|
130
|
+
slot,
|
|
131
|
+
title: section.title,
|
|
132
|
+
content: await section.build(invocation),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// One broken plugin section must not take the whole menu down.
|
|
137
|
+
logger.warn('command menu section failed to build', {
|
|
138
|
+
command: options.command,
|
|
139
|
+
section: id,
|
|
140
|
+
error: redact(error instanceof Error ? error.message : String(error)),
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
id,
|
|
144
|
+
slot,
|
|
145
|
+
title: section.title,
|
|
146
|
+
content: { lines: ['This section could not be loaded.'] },
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Every section, in the fixed slot order. */
|
|
151
|
+
async function sections(invocation) {
|
|
152
|
+
// Built-in sections read the store once; a fully replaced menu never
|
|
153
|
+
// touches it.
|
|
154
|
+
const built = store && builtSlots.length > 0
|
|
155
|
+
? await builtinSections({
|
|
156
|
+
store,
|
|
157
|
+
now,
|
|
158
|
+
...(options.extraLocks ? { extraLocks: options.extraLocks } : {}),
|
|
159
|
+
...(options.accounts ? { accounts: options.accounts } : {}),
|
|
160
|
+
...(options.quota ? { quota: options.quota } : {}),
|
|
161
|
+
...(options.routing ? { routing: options.routing } : {}),
|
|
162
|
+
...(options.limits ? { limits: options.limits } : {}),
|
|
163
|
+
})
|
|
164
|
+
: [];
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const slot of STORE_SLOTS) {
|
|
167
|
+
const replacement = replaced[slot];
|
|
168
|
+
if (replacement)
|
|
169
|
+
out.push(await plugin(slot, slot, replacement, invocation));
|
|
170
|
+
else {
|
|
171
|
+
const section = built.find((entry) => entry.slot === slot);
|
|
172
|
+
if (section)
|
|
173
|
+
out.push(section);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (options.cache)
|
|
177
|
+
out.push(await plugin('cache', 'cache', options.cache, invocation));
|
|
178
|
+
if (options.diagnostics)
|
|
179
|
+
out.push(await plugin('diagnostics', 'diagnostics', options.diagnostics, invocation));
|
|
180
|
+
for (const extra of extras)
|
|
181
|
+
out.push(await plugin(extra.id, 'extra', extra, invocation));
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
command: options.command,
|
|
186
|
+
async open(invocation) {
|
|
187
|
+
const own = ownInvocation(invocation, redact);
|
|
188
|
+
return dialogPayload(options.command, options.title, await sections(own), seam);
|
|
189
|
+
},
|
|
190
|
+
async apply(request, invocation) {
|
|
191
|
+
const own = ownInvocation(invocation, redact);
|
|
192
|
+
const finish = async (outcome) => applyResult(options.command, options.title, await sections(own), outcome, seam);
|
|
193
|
+
const action = request.command === options.command
|
|
194
|
+
? findAction(await sections(own), request)
|
|
195
|
+
: undefined;
|
|
196
|
+
if (!action)
|
|
197
|
+
return finish({
|
|
198
|
+
ok: false,
|
|
199
|
+
code: 'unavailable',
|
|
200
|
+
text: 'That action is no longer available.',
|
|
201
|
+
});
|
|
202
|
+
const confirmation = confirmationOf(action);
|
|
203
|
+
if (confirmation && request.confirmed !== true)
|
|
204
|
+
return finish({
|
|
205
|
+
ok: false,
|
|
206
|
+
code: 'needs-confirmation',
|
|
207
|
+
text: confirmation.message,
|
|
208
|
+
needsConfirmation: true,
|
|
209
|
+
});
|
|
210
|
+
const coerced = coerceValues(action.knobs ?? [], request.values);
|
|
211
|
+
if ('problem' in coerced)
|
|
212
|
+
return finish({
|
|
213
|
+
ok: false,
|
|
214
|
+
code: 'invalid-input',
|
|
215
|
+
text: coerced.problem,
|
|
216
|
+
});
|
|
217
|
+
let outcome;
|
|
218
|
+
try {
|
|
219
|
+
const result = await action.run({
|
|
220
|
+
values: coerced.values,
|
|
221
|
+
...(request.itemId !== undefined ? { itemId: request.itemId } : {}),
|
|
222
|
+
invocation: own,
|
|
223
|
+
});
|
|
224
|
+
outcome =
|
|
225
|
+
typeof result === 'string'
|
|
226
|
+
? { ok: true, text: result }
|
|
227
|
+
: {
|
|
228
|
+
ok: result.ok,
|
|
229
|
+
text: result.text,
|
|
230
|
+
...(result.ok ? {} : { code: result.code ?? 'refused' }),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
// The raw text goes only to the log (redacted); the user sees the
|
|
235
|
+
// projected code and message, never the exception's own words.
|
|
236
|
+
const failure = projectFailure(error);
|
|
237
|
+
logger.warn('command menu action failed', {
|
|
238
|
+
command: options.command,
|
|
239
|
+
section: request.sectionId,
|
|
240
|
+
action: request.actionId,
|
|
241
|
+
code: failure.code,
|
|
242
|
+
error: redact(error instanceof Error ? error.message : String(error)),
|
|
243
|
+
});
|
|
244
|
+
outcome = { ok: false, ...failure };
|
|
245
|
+
}
|
|
246
|
+
return finish(outcome);
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function isKnobValue(value) {
|
|
251
|
+
return (value === null ||
|
|
252
|
+
typeof value === 'string' ||
|
|
253
|
+
typeof value === 'boolean' ||
|
|
254
|
+
(typeof value === 'number' && Number.isFinite(value)));
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Checks an apply request that arrived over the loopback RPC; undefined when
|
|
258
|
+
* it is not one. Only the request's own fields are kept.
|
|
259
|
+
*/
|
|
260
|
+
export function parseApplyRequest(value) {
|
|
261
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
262
|
+
return undefined;
|
|
263
|
+
const raw = value;
|
|
264
|
+
if (typeof raw.command !== 'string' ||
|
|
265
|
+
typeof raw.sectionId !== 'string' ||
|
|
266
|
+
typeof raw.actionId !== 'string')
|
|
267
|
+
return undefined;
|
|
268
|
+
if (raw.itemId !== undefined && typeof raw.itemId !== 'string')
|
|
269
|
+
return undefined;
|
|
270
|
+
if (raw.sessionId !== undefined && typeof raw.sessionId !== 'string')
|
|
271
|
+
return undefined;
|
|
272
|
+
if (raw.confirmed !== undefined && typeof raw.confirmed !== 'boolean')
|
|
273
|
+
return undefined;
|
|
274
|
+
const values = {};
|
|
275
|
+
if (raw.values !== undefined) {
|
|
276
|
+
if (raw.values === null ||
|
|
277
|
+
typeof raw.values !== 'object' ||
|
|
278
|
+
Array.isArray(raw.values))
|
|
279
|
+
return undefined;
|
|
280
|
+
for (const [name, entry] of Object.entries(raw.values)) {
|
|
281
|
+
if (!isKnobValue(entry))
|
|
282
|
+
return undefined;
|
|
283
|
+
Object.defineProperty(values, name, {
|
|
284
|
+
value: entry,
|
|
285
|
+
enumerable: true,
|
|
286
|
+
writable: true,
|
|
287
|
+
configurable: true,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
command: raw.command,
|
|
293
|
+
sectionId: raw.sectionId,
|
|
294
|
+
actionId: raw.actionId,
|
|
295
|
+
...(typeof raw.itemId === 'string' ? { itemId: raw.itemId } : {}),
|
|
296
|
+
...(raw.values !== undefined ? { values } : {}),
|
|
297
|
+
...(raw.confirmed === true ? { confirmed: true } : {}),
|
|
298
|
+
...(typeof raw.sessionId === 'string' ? { sessionId: raw.sessionId } : {}),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/** The section slots, in the one order every renderer shows them. */
|
|
2
|
+
export declare const SECTION_SLOTS: readonly ['accounts', 'quota', 'routing', 'limits', 'cache', 'diagnostics', 'extra'];
|
|
3
|
+
export type SectionSlot = (typeof SECTION_SLOTS)[number];
|
|
4
|
+
/** One choice of a `choice` input. */
|
|
5
|
+
export interface MenuChoice {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A typed input an action collects before it runs. `value` is the current
|
|
11
|
+
* setting, shown as the default. A `number` or `text` input that is not
|
|
12
|
+
* `required` may be left empty, which the action receives as `null`.
|
|
13
|
+
*/
|
|
14
|
+
export type MenuKnob = {
|
|
15
|
+
kind: 'choice';
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
choices: MenuChoice[];
|
|
19
|
+
value?: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'toggle';
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
value: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'number';
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
value?: number;
|
|
30
|
+
min?: number;
|
|
31
|
+
max?: number;
|
|
32
|
+
required?: boolean;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'text';
|
|
35
|
+
id: string;
|
|
36
|
+
label: string;
|
|
37
|
+
value?: string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
/** Hide what is typed (a pasted secret, for example). */
|
|
40
|
+
masked?: boolean;
|
|
41
|
+
required?: boolean;
|
|
42
|
+
};
|
|
43
|
+
export type KnobValue = string | number | boolean | null;
|
|
44
|
+
export type KnobValues = Readonly<Record<string, KnobValue>>;
|
|
45
|
+
export interface MenuConfirmation {
|
|
46
|
+
message: string;
|
|
47
|
+
/** Irreversible actions always carry a confirmation. */
|
|
48
|
+
irreversible: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface MenuAction {
|
|
51
|
+
id: string;
|
|
52
|
+
label: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The header a section-level action is listed under (`Actions`). See
|
|
56
|
+
* `MenuSection` for how renderers draw groups.
|
|
57
|
+
*/
|
|
58
|
+
group?: string;
|
|
59
|
+
knobs: MenuKnob[];
|
|
60
|
+
/** Present when the action must be confirmed before it is applied. */
|
|
61
|
+
confirm?: MenuConfirmation;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The account fields a renderer may show, and nothing else: an account is
|
|
65
|
+
* always projected field by field, never spread, so a credential the store
|
|
66
|
+
* row carries cannot reach a payload.
|
|
67
|
+
*/
|
|
68
|
+
export interface MenuAccount {
|
|
69
|
+
id: string;
|
|
70
|
+
label?: string;
|
|
71
|
+
enabled: boolean;
|
|
72
|
+
type: 'oauth' | 'api';
|
|
73
|
+
/** What the plugin chose to show for the account's identity. */
|
|
74
|
+
identity?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface MenuItem {
|
|
77
|
+
id: string;
|
|
78
|
+
label: string;
|
|
79
|
+
detail?: string;
|
|
80
|
+
/** The header the item is listed under (`Accounts`); see `MenuSection`. */
|
|
81
|
+
group?: string;
|
|
82
|
+
/**
|
|
83
|
+
* A short value shown right-aligned beside the label (`enabled`,
|
|
84
|
+
* `5h 58% left`): an OpenCode dialog option's `footer`.
|
|
85
|
+
*/
|
|
86
|
+
status?: string;
|
|
87
|
+
account?: MenuAccount;
|
|
88
|
+
/**
|
|
89
|
+
* Extra name/value pairs a drawer may list under the item. Plugin data
|
|
90
|
+
* passes through here, so it is scrubbed of credential-shaped names.
|
|
91
|
+
*/
|
|
92
|
+
facts?: Record<string, unknown>;
|
|
93
|
+
actions: MenuAction[];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* One section of the menu. How every renderer draws it:
|
|
97
|
+
*
|
|
98
|
+
* - Every option a renderer shows can be selected and does something: it is
|
|
99
|
+
* an item with at least one action, or a section-level action. Text that
|
|
100
|
+
* does nothing is never an option; it is a group header or a line.
|
|
101
|
+
* - `lines` are read-only text. On OpenCode they are drawn as header text
|
|
102
|
+
* (a `category`) above the first group; on Pi they are part of the select
|
|
103
|
+
* title.
|
|
104
|
+
* - The options are the items, then the section-level actions, in that
|
|
105
|
+
* order. When an option's `group` differs from the previous one's, a
|
|
106
|
+
* header naming the group is drawn first. A header is never selectable
|
|
107
|
+
* (on OpenCode it is the option's `category`).
|
|
108
|
+
* - An item's `status` is drawn right-aligned beside its label (on
|
|
109
|
+
* OpenCode, `footer`); its `detail` and an action's `description` are
|
|
110
|
+
* the secondary text (`description`).
|
|
111
|
+
*
|
|
112
|
+
* The built-in sections never emit an item without actions, and a plugin
|
|
113
|
+
* section should not either. A renderer that meets one must not offer it as
|
|
114
|
+
* an option: on OpenCode a `disabled` option is hidden altogether, so the
|
|
115
|
+
* item is written as header text instead, and Pi writes it into the title.
|
|
116
|
+
*/
|
|
117
|
+
export interface MenuSection {
|
|
118
|
+
id: string;
|
|
119
|
+
slot: SectionSlot;
|
|
120
|
+
title: string;
|
|
121
|
+
/** Read-only text shown above the rows; never a selectable option. */
|
|
122
|
+
lines: string[];
|
|
123
|
+
items: MenuItem[];
|
|
124
|
+
actions: MenuAction[];
|
|
125
|
+
facts?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
export interface CommandMenuModel {
|
|
128
|
+
/** The slash command's name, without the slash. */
|
|
129
|
+
command: string;
|
|
130
|
+
title: string;
|
|
131
|
+
sections: MenuSection[];
|
|
132
|
+
}
|
|
133
|
+
/** What a host's TUI receives when the slash command opens the dialog. */
|
|
134
|
+
export interface CommandDialogPayload {
|
|
135
|
+
command: string;
|
|
136
|
+
menu: CommandMenuModel;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* One applied action, as a renderer sends it back. `itemId` names the item
|
|
140
|
+
* the action sits on; absent for a section-level action.
|
|
141
|
+
*/
|
|
142
|
+
export interface CommandApplyRequest {
|
|
143
|
+
command: string;
|
|
144
|
+
sectionId: string;
|
|
145
|
+
itemId?: string;
|
|
146
|
+
actionId: string;
|
|
147
|
+
values?: KnobValues;
|
|
148
|
+
/** True once the user confirmed an action that carries a confirmation. */
|
|
149
|
+
confirmed?: boolean;
|
|
150
|
+
sessionId?: string;
|
|
151
|
+
}
|
|
152
|
+
/** The outcome of an apply: a message for the user and the refreshed menu. */
|
|
153
|
+
export interface CommandApplyResult {
|
|
154
|
+
command: string;
|
|
155
|
+
ok: boolean;
|
|
156
|
+
text: string;
|
|
157
|
+
/**
|
|
158
|
+
* A stable code naming why the apply failed; present on every failure.
|
|
159
|
+
* The library's own codes are `unavailable`, `needs-confirmation`,
|
|
160
|
+
* `invalid-input`, `refused`, `action-failed` and `pool-<store failure
|
|
161
|
+
* kind>`; a plugin's `CommandError` or `ActionOutcome` names its own.
|
|
162
|
+
*/
|
|
163
|
+
code?: string;
|
|
164
|
+
/** True when the action was refused only for want of a confirmation. */
|
|
165
|
+
needsConfirmation?: boolean;
|
|
166
|
+
menu: CommandMenuModel;
|
|
167
|
+
}
|
|
168
|
+
export type NotifyKind = 'info' | 'warning' | 'error';
|
|
169
|
+
/**
|
|
170
|
+
* The per-invocation context of one slash-command call. The library reads
|
|
171
|
+
* both fields once, when the call starts, and keeps that copy for any work
|
|
172
|
+
* the call leaves running (a login completing later, for example), so a host
|
|
173
|
+
* that reuses one context object across sessions cannot reroute feedback.
|
|
174
|
+
*/
|
|
175
|
+
export interface CommandInvocation {
|
|
176
|
+
sessionId?: string;
|
|
177
|
+
notify(message: string, kind?: NotifyKind): void;
|
|
178
|
+
}
|
|
179
|
+
export interface ActionOutcome {
|
|
180
|
+
ok: boolean;
|
|
181
|
+
text: string;
|
|
182
|
+
/** The failure's stable code; a failure without one is `refused`. */
|
|
183
|
+
code?: string;
|
|
184
|
+
}
|
|
185
|
+
export interface ActionInput {
|
|
186
|
+
values: KnobValues;
|
|
187
|
+
/** The item the action sits on; absent for a section-level action. */
|
|
188
|
+
itemId?: string;
|
|
189
|
+
invocation: CommandInvocation;
|
|
190
|
+
}
|
|
191
|
+
type ActionBase = {
|
|
192
|
+
id: string;
|
|
193
|
+
label: string;
|
|
194
|
+
description?: string;
|
|
195
|
+
/** The header a section-level action is listed under; see `MenuSection`. */
|
|
196
|
+
group?: string;
|
|
197
|
+
knobs?: MenuKnob[];
|
|
198
|
+
run(input: ActionInput): Promise<string | ActionOutcome>;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* An action and its body. An irreversible action must name its
|
|
202
|
+
* confirmation; a reversible one may.
|
|
203
|
+
*/
|
|
204
|
+
export type ActionDefinition = ActionBase & ({
|
|
205
|
+
irreversible: true;
|
|
206
|
+
confirm: string;
|
|
207
|
+
} | {
|
|
208
|
+
irreversible?: false;
|
|
209
|
+
confirm?: string;
|
|
210
|
+
});
|
|
211
|
+
export interface ItemDefinition {
|
|
212
|
+
id: string;
|
|
213
|
+
label: string;
|
|
214
|
+
detail?: string;
|
|
215
|
+
/** The header the item is listed under; see `MenuSection`. */
|
|
216
|
+
group?: string;
|
|
217
|
+
/** A short value shown right-aligned beside the label. */
|
|
218
|
+
status?: string;
|
|
219
|
+
facts?: Record<string, unknown>;
|
|
220
|
+
actions?: ActionDefinition[];
|
|
221
|
+
}
|
|
222
|
+
export interface SectionContent {
|
|
223
|
+
/** Read-only text; renderers never make a line selectable. */
|
|
224
|
+
lines?: string[];
|
|
225
|
+
items?: ItemDefinition[];
|
|
226
|
+
actions?: ActionDefinition[];
|
|
227
|
+
facts?: Record<string, unknown>;
|
|
228
|
+
}
|
|
229
|
+
/** A section a plugin supplies for one of its slots. */
|
|
230
|
+
export interface PluginSection {
|
|
231
|
+
title: string;
|
|
232
|
+
build(invocation: CommandInvocation): SectionContent | Promise<SectionContent>;
|
|
233
|
+
}
|
|
234
|
+
/** A provider extra: a plugin section shown after the fixed slots. */
|
|
235
|
+
export interface PluginExtraSection extends PluginSection {
|
|
236
|
+
id: string;
|
|
237
|
+
}
|
|
238
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// The command menu: sections of items, each carrying actions with typed
|
|
2
|
+
// inputs. Two kinds of type live here. The `Menu*` types and the request and
|
|
3
|
+
// result types are plain data: they cross the loopback RPC to a host's TUI
|
|
4
|
+
// and are what an in-process renderer walks. The `*Definition` types carry
|
|
5
|
+
// the action bodies a plugin or the library supplies; they never leave the
|
|
6
|
+
// process and are turned into the plain types only by the seam in `seam.ts`.
|
|
7
|
+
/** The section slots, in the one order every renderer shows them. */
|
|
8
|
+
export const SECTION_SLOTS = [
|
|
9
|
+
'accounts',
|
|
10
|
+
'quota',
|
|
11
|
+
'routing',
|
|
12
|
+
'limits',
|
|
13
|
+
'cache',
|
|
14
|
+
'diagnostics',
|
|
15
|
+
'extra',
|
|
16
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CommandMenu } from './menu.js';
|
|
2
|
+
import type { NotifyKind } from './model.js';
|
|
3
|
+
/**
|
|
4
|
+
* The part of Pi's `ExtensionUIContext` the renderer uses. `select` and
|
|
5
|
+
* `input` resolve undefined when the user backs out.
|
|
6
|
+
*/
|
|
7
|
+
export interface PiMenuUi {
|
|
8
|
+
select(title: string, options: string[]): Promise<string | undefined>;
|
|
9
|
+
confirm(title: string, message: string): Promise<boolean>;
|
|
10
|
+
input(title: string, placeholder?: string): Promise<string | undefined>;
|
|
11
|
+
notify(message: string, type?: NotifyKind): void;
|
|
12
|
+
}
|
|
13
|
+
export interface PiMenuOptions {
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Runs one invocation of the slash command on Pi: sections, then an item or
|
|
18
|
+
* a section action, then the action's inputs and confirmation; after each
|
|
19
|
+
* apply the user is back in the same section with the refreshed menu. Each
|
|
20
|
+
* section's list ends with Back, which returns to the section list; backing
|
|
21
|
+
* out of the section list ends the invocation.
|
|
22
|
+
*/
|
|
23
|
+
export declare function runPiCommandMenu(menu: CommandMenu, ui: PiMenuUi, options?: PiMenuOptions): Promise<void>;
|