@noodleseed/one 0.153.0 → 0.155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/app-purge-reconciliation-args.d.ts +28 -0
- package/dist/commands/app-purge-reconciliation-args.d.ts.map +1 -0
- package/dist/commands/app-purge-reconciliation-args.js +124 -0
- package/dist/commands/app-purge-reconciliation-args.js.map +1 -0
- package/dist/commands/app-purge-reconciliation-files.d.ts +8 -0
- package/dist/commands/app-purge-reconciliation-files.d.ts.map +1 -0
- package/dist/commands/app-purge-reconciliation-files.js +109 -0
- package/dist/commands/app-purge-reconciliation-files.js.map +1 -0
- package/dist/commands/app-purge-reconciliation-ops.d.ts +3 -0
- package/dist/commands/app-purge-reconciliation-ops.d.ts.map +1 -0
- package/dist/commands/app-purge-reconciliation-ops.js +190 -0
- package/dist/commands/app-purge-reconciliation-ops.js.map +1 -0
- package/dist/commands/billing-ops.d.ts.map +1 -1
- package/dist/commands/billing-ops.js +7 -0
- package/dist/commands/billing-ops.js.map +1 -1
- package/dist/commands/billing-org-transfer-files.d.ts +12 -0
- package/dist/commands/billing-org-transfer-files.d.ts.map +1 -0
- package/dist/commands/billing-org-transfer-files.js +112 -0
- package/dist/commands/billing-org-transfer-files.js.map +1 -0
- package/dist/commands/billing-org-transfer-ops.d.ts +4 -0
- package/dist/commands/billing-org-transfer-ops.d.ts.map +1 -0
- package/dist/commands/billing-org-transfer-ops.js +534 -0
- package/dist/commands/billing-org-transfer-ops.js.map +1 -0
- package/dist/commands/service-ops.d.ts.map +1 -1
- package/dist/commands/service-ops.js +4 -1
- package/dist/commands/service-ops.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +2 -0
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-suggestions.js +5 -3
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-bounds.d.ts +36 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-bounds.js +52 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-store.d.ts +115 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-store.js +51 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-continuity-store.d.ts +26 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-continuity-store.js +80 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +1 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +2 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-response-values.js +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-responses.js +1 -10
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-stream.js +1 -10
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +35 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +4 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-account.js +96 -0
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-billing.js +116 -1
- package/node_modules/@noodle-borg/compiler/dist/manifest/branding-schema.js +86 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +44 -25
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +24 -76
- package/node_modules/@noodle-borg/control-plane/dist/app-purge-reconciliation-port.d.ts +25 -0
- package/node_modules/@noodle-borg/control-plane/dist/app-purge-reconciliation-port.js +10 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +1 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
- package/node_modules/@noodle-borg/control-plane/package.json +3 -1
- package/node_modules/@noodle-borg/service/dist/routes/app-purge-reconciliation.js +72 -0
- package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +15 -2
- package/node_modules/@noodle-borg/service/dist/service.js +27 -1
- package/node_modules/@noodle-borg/service/dist/store/in-memory.js +4 -8
- package/node_modules/@noodle-borg/service/dist/store/json-file.js +6 -3
- package/node_modules/@noodle-borg/service/dist/store/records.js +20 -0
- package/node_modules/@noodle-borg/service/dist/store.d.ts +3 -2
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/app-purge-reconciliation.d.ts +177 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/app-purge-reconciliation.js +189 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/billing-org-transfer.d.ts +629 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/billing-org-transfer.js +259 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +21 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +23 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/platform-account-reset.d.ts +4 -4
- package/node_modules/@noodleseed/assistant/package.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type AssistantContinuityClaim, type AssistantContinuityContext, type AssistantContinuityRecord, type AssistantContinuityStore } from './continuity-store.js';
|
|
2
|
+
import type { TenantRef } from './tenant-ref.js';
|
|
3
|
+
/** The in-memory half of the pair. Both run the shared parity suite; neither is the only proof. */
|
|
4
|
+
export declare class InMemoryAssistantContinuityStore implements AssistantContinuityStore {
|
|
5
|
+
#private;
|
|
6
|
+
issue(input: {
|
|
7
|
+
readonly sessionId: string;
|
|
8
|
+
readonly tenant: TenantRef;
|
|
9
|
+
readonly context: AssistantContinuityContext;
|
|
10
|
+
readonly windowMs?: number;
|
|
11
|
+
readonly maxRestores?: number;
|
|
12
|
+
readonly now: Date;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
readonly record: AssistantContinuityRecord;
|
|
15
|
+
readonly handle: string;
|
|
16
|
+
} | undefined>;
|
|
17
|
+
claim(input: {
|
|
18
|
+
readonly handle: string;
|
|
19
|
+
readonly context: AssistantContinuityContext;
|
|
20
|
+
readonly now: Date;
|
|
21
|
+
}): Promise<AssistantContinuityClaim>;
|
|
22
|
+
sweepExpired(input: {
|
|
23
|
+
readonly now: Date;
|
|
24
|
+
}): Promise<number>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=in-memory-continuity-store.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { continuityDigest, continuityHandle, continuityHashesEqual, continuityMaxRestores, continuityWindowMs, sameContinuityContext, } from './continuity-store.js';
|
|
3
|
+
/** The in-memory half of the pair. Both run the shared parity suite; neither is the only proof. */
|
|
4
|
+
export class InMemoryAssistantContinuityStore {
|
|
5
|
+
#rows = new Map();
|
|
6
|
+
async issue(input) {
|
|
7
|
+
return this.#write({ ...input, restoreCount: 0 });
|
|
8
|
+
}
|
|
9
|
+
async claim(input) {
|
|
10
|
+
const hash = continuityDigest(input.handle);
|
|
11
|
+
const found = [...this.#rows.values()].find((row) => continuityHashesEqual(row.handleHash, hash));
|
|
12
|
+
if (!found || found.claimedAt !== undefined)
|
|
13
|
+
return { ok: false, reason: 'unknown' };
|
|
14
|
+
// Context before expiry: a handle replayed from somewhere it was never issued should read as the
|
|
15
|
+
// exfiltration signal it is, whether or not the value happened to be stale by then.
|
|
16
|
+
if (!sameContinuityContext(found.context, input.context)) {
|
|
17
|
+
return { ok: false, reason: 'context_mismatch' };
|
|
18
|
+
}
|
|
19
|
+
if (Date.parse(found.expiresAt) <= input.now.getTime())
|
|
20
|
+
return { ok: false, reason: 'expired' };
|
|
21
|
+
const restoreCount = found.restoreCount + 1;
|
|
22
|
+
const claimed = {
|
|
23
|
+
...found,
|
|
24
|
+
restoreCount,
|
|
25
|
+
claimedAt: input.now.toISOString(),
|
|
26
|
+
};
|
|
27
|
+
this.#rows.set(found.id, claimed);
|
|
28
|
+
if (restoreCount >= found.maxRestores)
|
|
29
|
+
return { ok: true, record: claimed };
|
|
30
|
+
const rotated = this.#write({
|
|
31
|
+
sessionId: found.sessionId,
|
|
32
|
+
tenant: found.tenant,
|
|
33
|
+
context: found.context,
|
|
34
|
+
// Re-derive from the record so a rotation can never widen what the first issue clamped.
|
|
35
|
+
windowMs: Date.parse(found.expiresAt) - Date.parse(found.createdAt),
|
|
36
|
+
maxRestores: found.maxRestores,
|
|
37
|
+
restoreCount,
|
|
38
|
+
now: input.now,
|
|
39
|
+
});
|
|
40
|
+
return rotated === undefined
|
|
41
|
+
? { ok: true, record: claimed }
|
|
42
|
+
: { ok: true, record: claimed, handle: rotated.handle };
|
|
43
|
+
}
|
|
44
|
+
async sweepExpired(input) {
|
|
45
|
+
let removed = 0;
|
|
46
|
+
for (const [id, row] of this.#rows) {
|
|
47
|
+
if (row.claimedAt !== undefined || Date.parse(row.expiresAt) <= input.now.getTime()) {
|
|
48
|
+
this.#rows.delete(id);
|
|
49
|
+
removed++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return removed;
|
|
53
|
+
}
|
|
54
|
+
#write(input) {
|
|
55
|
+
const windowMs = continuityWindowMs(input.windowMs);
|
|
56
|
+
const maxRestores = continuityMaxRestores(input.maxRestores);
|
|
57
|
+
if (windowMs === 0 || maxRestores === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
// Supersede rather than accumulate: one live handle per session.
|
|
60
|
+
for (const [id, row] of this.#rows) {
|
|
61
|
+
if (row.sessionId === input.sessionId && row.claimedAt === undefined)
|
|
62
|
+
this.#rows.delete(id);
|
|
63
|
+
}
|
|
64
|
+
const handle = continuityHandle();
|
|
65
|
+
const record = {
|
|
66
|
+
id: `cont_${randomUUID().replaceAll('-', '')}`,
|
|
67
|
+
sessionId: input.sessionId,
|
|
68
|
+
tenant: input.tenant,
|
|
69
|
+
context: input.context,
|
|
70
|
+
handleHash: continuityDigest(handle),
|
|
71
|
+
restoreCount: input.restoreCount,
|
|
72
|
+
maxRestores,
|
|
73
|
+
createdAt: input.now.toISOString(),
|
|
74
|
+
expiresAt: new Date(input.now.getTime() + windowMs).toISOString(),
|
|
75
|
+
};
|
|
76
|
+
this.#rows.set(record.id, record);
|
|
77
|
+
return { record, handle };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=in-memory-continuity-store.js.map
|
|
@@ -94,6 +94,7 @@ export declare function requestModelCompletion(input: {
|
|
|
94
94
|
readonly messages: readonly AssistantModelMessage[];
|
|
95
95
|
readonly tools: readonly AssistantModelTool[];
|
|
96
96
|
readonly toolChoice?: 'auto' | 'none' | 'required';
|
|
97
|
+
readonly jsonOutput?: boolean;
|
|
97
98
|
readonly fetcher: (url: string, init: RequestInit) => Promise<Response>;
|
|
98
99
|
readonly onContent?: (delta: string) => void;
|
|
99
100
|
readonly maxResponseBytes?: number;
|
|
@@ -17,6 +17,7 @@ export async function requestModelCompletion(input) {
|
|
|
17
17
|
store: false,
|
|
18
18
|
input: responsesInput(input.messages),
|
|
19
19
|
tools: responsesTools(input.tools),
|
|
20
|
+
...(input.jsonOutput ? { text: { format: { type: 'json_object' } } } : {}),
|
|
20
21
|
...(input.toolChoice === undefined ? {} : { tool_choice: input.toolChoice }),
|
|
21
22
|
...(completionLimit === undefined ? {} : { max_output_tokens: completionLimit }),
|
|
22
23
|
}
|
|
@@ -26,6 +27,7 @@ export async function requestModelCompletion(input) {
|
|
|
26
27
|
stream: true,
|
|
27
28
|
messages: input.messages,
|
|
28
29
|
tools: input.tools,
|
|
30
|
+
...(input.jsonOutput ? { response_format: { type: 'json_object' } } : {}),
|
|
29
31
|
...(input.toolChoice === undefined ? {} : { tool_choice: input.toolChoice }),
|
|
30
32
|
...(completionLimit === undefined ? {} : { max_completion_tokens: completionLimit }),
|
|
31
33
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function nonNegativeInteger(value) {
|
|
2
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
3
|
+
}
|
|
4
|
+
export async function readBoundedText(response, maxBytes) {
|
|
5
|
+
const text = await response.text();
|
|
6
|
+
if (new TextEncoder().encode(text).byteLength > maxBytes) {
|
|
7
|
+
throw new Error('model response too large');
|
|
8
|
+
}
|
|
9
|
+
return text;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=model-response-values.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nonNegativeInteger, readBoundedText } from './model-response-values.js';
|
|
1
2
|
export function responsesInput(messages) {
|
|
2
3
|
return messages.flatMap((message) => {
|
|
3
4
|
if (message.role === 'tool') {
|
|
@@ -150,14 +151,4 @@ function parseRecord(value) {
|
|
|
150
151
|
}
|
|
151
152
|
return value;
|
|
152
153
|
}
|
|
153
|
-
function nonNegativeInteger(value) {
|
|
154
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
155
|
-
}
|
|
156
|
-
async function readBoundedText(response, maxBytes) {
|
|
157
|
-
const text = await response.text();
|
|
158
|
-
if (new TextEncoder().encode(text).byteLength > maxBytes) {
|
|
159
|
-
throw new Error('model response too large');
|
|
160
|
-
}
|
|
161
|
-
return text;
|
|
162
|
-
}
|
|
163
154
|
//# sourceMappingURL=model-responses.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nonNegativeInteger, readBoundedText } from './model-response-values.js';
|
|
1
2
|
export async function readModelCompletion(response, onContent, maxBytes = 1 << 20) {
|
|
2
3
|
const contentType = response.headers.get('content-type') ?? '';
|
|
3
4
|
if (!contentType.includes('text/event-stream')) {
|
|
@@ -111,14 +112,4 @@ function normalizeUsageValue(usage) {
|
|
|
111
112
|
...(reasoningTokens === undefined ? {} : { reasoningTokens }),
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
|
-
function nonNegativeInteger(value) {
|
|
115
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
116
|
-
}
|
|
117
|
-
async function readBoundedText(response, maxBytes) {
|
|
118
|
-
const text = await response.text();
|
|
119
|
-
if (new TextEncoder().encode(text).byteLength > maxBytes) {
|
|
120
|
-
throw new Error('model response too large');
|
|
121
|
-
}
|
|
122
|
-
return text;
|
|
123
|
-
}
|
|
124
115
|
//# sourceMappingURL=model-stream.js.map
|
|
@@ -17,12 +17,15 @@ export * from './assistant-store.js';
|
|
|
17
17
|
export * from './assistant-transcript.js';
|
|
18
18
|
export * from './assistant-transcript-events.js';
|
|
19
19
|
export * from './assistant-view-availability.js';
|
|
20
|
+
export * from './continuity-bounds.js';
|
|
21
|
+
export * from './continuity-store.js';
|
|
20
22
|
export * from './elevation.js';
|
|
21
23
|
export * from './elevation-store.js';
|
|
22
24
|
export * from './embed-operator-view.js';
|
|
23
25
|
export * from './embed-script.js';
|
|
24
26
|
export * from './embed-store.js';
|
|
25
27
|
export * from './in-memory-assistant-appearance-store.js';
|
|
28
|
+
export * from './in-memory-continuity-store.js';
|
|
26
29
|
export * from './in-memory-elevation-store.js';
|
|
27
30
|
export * from './in-memory-embed-store.js';
|
|
28
31
|
export * from './managed-spend.js';
|
|
@@ -17,12 +17,15 @@ export * from './assistant-store.js';
|
|
|
17
17
|
export * from './assistant-transcript.js';
|
|
18
18
|
export * from './assistant-transcript-events.js';
|
|
19
19
|
export * from './assistant-view-availability.js';
|
|
20
|
+
export * from './continuity-bounds.js';
|
|
21
|
+
export * from './continuity-store.js';
|
|
20
22
|
export * from './elevation.js';
|
|
21
23
|
export * from './elevation-store.js';
|
|
22
24
|
export * from './embed-operator-view.js';
|
|
23
25
|
export * from './embed-script.js';
|
|
24
26
|
export * from './embed-store.js';
|
|
25
27
|
export * from './in-memory-assistant-appearance-store.js';
|
|
28
|
+
export * from './in-memory-continuity-store.js';
|
|
26
29
|
export * from './in-memory-elevation-store.js';
|
|
27
30
|
export * from './in-memory-embed-store.js';
|
|
28
31
|
export * from './managed-spend.js';
|
|
@@ -173,6 +173,31 @@ export interface AuthenticatedWebsiteAccess {
|
|
|
173
173
|
readonly enabled?: boolean;
|
|
174
174
|
};
|
|
175
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Anonymous cross-page display continuity for one public surface (ADR 0223, clauses 11-16).
|
|
178
|
+
*
|
|
179
|
+
* Off unless asked for. Anonymous conversation text is your content on your page, so whether it
|
|
180
|
+
* survives a navigation is your call rather than a default the platform imposes.
|
|
181
|
+
*
|
|
182
|
+
* What a visitor gets is the text they have already read, re-rendered on the next page, on a fresh
|
|
183
|
+
* session. What it never grants is the old session itself: no tool authority, no share of a spent
|
|
184
|
+
* turn budget, and no pending confirmation carried across — a capability that survives a navigation
|
|
185
|
+
* is exactly what this design refuses to create.
|
|
186
|
+
*/
|
|
187
|
+
export interface AssistantContinuityDeclaration {
|
|
188
|
+
readonly enabled?: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* How long a handle stays valid after the turn that issued it, measured from when the visitor last
|
|
191
|
+
* spoke. Defaults to 300; 600 is the structural ceiling, and 0 disables continuity outright. An
|
|
192
|
+
* operator may lower what you declare here and can never raise it.
|
|
193
|
+
*/
|
|
194
|
+
readonly windowSeconds?: number;
|
|
195
|
+
/**
|
|
196
|
+
* How many times one conversation may be restored before continuity ends and the next page starts
|
|
197
|
+
* fresh. Defaults to 3, ceiling 10, and 0 disables continuity outright.
|
|
198
|
+
*/
|
|
199
|
+
readonly maxRestores?: number;
|
|
200
|
+
}
|
|
176
201
|
export interface PublicWebsiteAccess {
|
|
177
202
|
readonly mode: 'public' | 'mixed';
|
|
178
203
|
readonly origins: readonly string[];
|
|
@@ -182,6 +207,8 @@ export interface PublicWebsiteAccess {
|
|
|
182
207
|
readonly webmcp?: {
|
|
183
208
|
readonly enabled?: boolean;
|
|
184
209
|
};
|
|
210
|
+
/** @see PublicWebsiteInput.continuity */
|
|
211
|
+
readonly continuity?: AssistantContinuityDeclaration;
|
|
185
212
|
}
|
|
186
213
|
export type AssistantAccess = AuthenticatedWebsiteAccess | PublicWebsiteAccess;
|
|
187
214
|
export interface AuthenticatedWebsiteInput {
|
|
@@ -226,6 +253,14 @@ export interface PublicWebsiteInput {
|
|
|
226
253
|
readonly webmcp?: {
|
|
227
254
|
readonly enabled?: boolean;
|
|
228
255
|
};
|
|
256
|
+
/**
|
|
257
|
+
* Let an anonymous visitor keep the conversation they can see when they navigate to another page
|
|
258
|
+
* of this site. Omitted means off, and no existing embed changes behavior.
|
|
259
|
+
*
|
|
260
|
+
* Declared only here, never on an authenticated surface: that direction reattaches through a
|
|
261
|
+
* backend-verified sign-in instead, so a declaration there would configure nothing.
|
|
262
|
+
*/
|
|
263
|
+
readonly continuity?: AssistantContinuityDeclaration;
|
|
229
264
|
}
|
|
230
265
|
export declare function authenticatedWebsite(input: AuthenticatedWebsiteInput): AuthenticatedWebsiteAccess;
|
|
231
266
|
export declare function publicWebsite(input: PublicWebsiteInput): PublicWebsiteAccess;
|
|
@@ -16,6 +16,7 @@ export function publicWebsite(input) {
|
|
|
16
16
|
capabilities: [...input.capabilities],
|
|
17
17
|
...(input.instructions === undefined ? {} : { instructions: input.instructions }),
|
|
18
18
|
...(input.webmcp === undefined ? {} : { webmcp: { ...input.webmcp } }),
|
|
19
|
+
...(input.continuity === undefined ? {} : { continuity: { ...input.continuity } }),
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
function serializeOrigins(origins, path) {
|
|
@@ -59,6 +60,9 @@ export function embeddedAssistant(input) {
|
|
|
59
60
|
? { sessionClaims: structuredClone(surface.sessionClaims) }
|
|
60
61
|
: {}),
|
|
61
62
|
...(surface.webmcp === undefined ? {} : { webmcp: { ...surface.webmcp } }),
|
|
63
|
+
...(surface.mode !== 'authenticated' && surface.continuity !== undefined
|
|
64
|
+
? { continuity: { ...surface.continuity } }
|
|
65
|
+
: {}),
|
|
62
66
|
})),
|
|
63
67
|
allowedOrigins: surfaces.flatMap((surface) => [...surface.origins]),
|
|
64
68
|
...structuredClone(ui),
|
|
@@ -26,6 +26,11 @@ const JSON_FLAG = {
|
|
|
26
26
|
type: 'boolean',
|
|
27
27
|
summary: 'Emit JSON output.',
|
|
28
28
|
};
|
|
29
|
+
const REQUIRED_STRING_FLAG = {
|
|
30
|
+
...OPTIONAL_FLAG,
|
|
31
|
+
type: 'string',
|
|
32
|
+
required: true,
|
|
33
|
+
};
|
|
29
34
|
const CAPABILITY_NAMES = [
|
|
30
35
|
'identity',
|
|
31
36
|
'access',
|
|
@@ -190,6 +195,97 @@ export const CATALOG_ACCOUNT = [
|
|
|
190
195
|
],
|
|
191
196
|
jsonOutput: { mode: 'single' },
|
|
192
197
|
},
|
|
198
|
+
{
|
|
199
|
+
name: 'app-purge',
|
|
200
|
+
summary: 'Reconcile historical app anchors after an already-recorded retention purge.',
|
|
201
|
+
arguments: [],
|
|
202
|
+
flags: [],
|
|
203
|
+
subcommands: [
|
|
204
|
+
{
|
|
205
|
+
name: 'preview',
|
|
206
|
+
summary: 'Write a bounded exact-set reconciliation preview artifact.',
|
|
207
|
+
arguments: [],
|
|
208
|
+
flags: [
|
|
209
|
+
{
|
|
210
|
+
...REQUIRED_STRING_FLAG,
|
|
211
|
+
name: 'output',
|
|
212
|
+
value: '<absolute-path>',
|
|
213
|
+
summary: 'New absolute path for the exclusive 0600 preview artifact.',
|
|
214
|
+
sensitive: false,
|
|
215
|
+
},
|
|
216
|
+
SERVICE,
|
|
217
|
+
AUTH_TOKEN,
|
|
218
|
+
JSON_FLAG,
|
|
219
|
+
],
|
|
220
|
+
jsonOutput: { mode: 'single' },
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'apply',
|
|
224
|
+
summary: 'Apply one approved exact preview with recovery and approval evidence.',
|
|
225
|
+
arguments: [],
|
|
226
|
+
flags: [
|
|
227
|
+
{
|
|
228
|
+
...REQUIRED_STRING_FLAG,
|
|
229
|
+
name: 'approved-preview',
|
|
230
|
+
value: '<absolute-path>',
|
|
231
|
+
summary: 'Absolute path to the approved exact 0600 preview artifact.',
|
|
232
|
+
sensitive: true,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
...REQUIRED_STRING_FLAG,
|
|
236
|
+
name: 'release-sha',
|
|
237
|
+
value: '<sha>',
|
|
238
|
+
summary: 'Exact deployed System Release SHA.',
|
|
239
|
+
sensitive: false,
|
|
240
|
+
constraints: { minLength: 40, maxLength: 40 },
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
...REQUIRED_STRING_FLAG,
|
|
244
|
+
name: 'approval-reference',
|
|
245
|
+
value: '<reference>',
|
|
246
|
+
summary: 'Restricted founder or product-owner approval reference.',
|
|
247
|
+
sensitive: true,
|
|
248
|
+
constraints: { minLength: 1, maxLength: 256 },
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
...REQUIRED_STRING_FLAG,
|
|
252
|
+
name: 'recovery-checkpoint',
|
|
253
|
+
value: '<reference>',
|
|
254
|
+
summary: 'Verified backup or point-in-time recovery checkpoint reference.',
|
|
255
|
+
sensitive: true,
|
|
256
|
+
constraints: { minLength: 1, maxLength: 256 },
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
...REQUIRED_STRING_FLAG,
|
|
260
|
+
name: 'reason',
|
|
261
|
+
value: '<reason>',
|
|
262
|
+
summary: 'Bounded reconciliation reason.',
|
|
263
|
+
sensitive: true,
|
|
264
|
+
constraints: { minLength: 1, maxLength: 500 },
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
...REQUIRED_STRING_FLAG,
|
|
268
|
+
name: 'idempotency-key',
|
|
269
|
+
value: '<key>',
|
|
270
|
+
summary: 'Private key for exact replay.',
|
|
271
|
+
sensitive: true,
|
|
272
|
+
constraints: { minLength: 8, maxLength: 256 },
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
...OPTIONAL_FLAG,
|
|
276
|
+
name: 'yes',
|
|
277
|
+
type: 'boolean',
|
|
278
|
+
summary: 'Confirm the approved destructive operation.',
|
|
279
|
+
required: true,
|
|
280
|
+
},
|
|
281
|
+
SERVICE,
|
|
282
|
+
AUTH_TOKEN,
|
|
283
|
+
JSON_FLAG,
|
|
284
|
+
],
|
|
285
|
+
jsonOutput: { mode: 'single' },
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
},
|
|
193
289
|
],
|
|
194
290
|
},
|
|
195
291
|
{
|
|
@@ -62,6 +62,75 @@ const IDEMPOTENCY_FLAG = flag({
|
|
|
62
62
|
required: true,
|
|
63
63
|
sensitive: true,
|
|
64
64
|
});
|
|
65
|
+
const TRANSFER_ACCOUNT_FLAG = flag({
|
|
66
|
+
...FLAG_FIELDS,
|
|
67
|
+
name: 'account',
|
|
68
|
+
type: 'string',
|
|
69
|
+
value: '<destination-id>',
|
|
70
|
+
summary: 'Destination billing account identifier.',
|
|
71
|
+
required: true,
|
|
72
|
+
});
|
|
73
|
+
const TRANSFER_ORG_FLAG = flag({
|
|
74
|
+
...FLAG_FIELDS,
|
|
75
|
+
name: 'org',
|
|
76
|
+
type: 'string',
|
|
77
|
+
value: '<org>',
|
|
78
|
+
summary: 'Organization slug to transfer.',
|
|
79
|
+
required: true,
|
|
80
|
+
});
|
|
81
|
+
const TRANSFER_QUERY_FLAG = flag({
|
|
82
|
+
...FLAG_FIELDS,
|
|
83
|
+
name: 'query',
|
|
84
|
+
type: 'string',
|
|
85
|
+
value: '<text>',
|
|
86
|
+
summary: 'Filter owned organizations by name or slug.',
|
|
87
|
+
constraints: { minLength: 1, maxLength: 100 },
|
|
88
|
+
});
|
|
89
|
+
const TRANSFER_CURSOR_FLAG = flag({
|
|
90
|
+
...FLAG_FIELDS,
|
|
91
|
+
name: 'cursor',
|
|
92
|
+
type: 'string',
|
|
93
|
+
value: '<cursor>',
|
|
94
|
+
summary: 'Opaque continuation cursor.',
|
|
95
|
+
constraints: { maxLength: 512 },
|
|
96
|
+
});
|
|
97
|
+
const TRANSFER_LIMIT_FLAG = flag({
|
|
98
|
+
...FLAG_FIELDS,
|
|
99
|
+
name: 'limit',
|
|
100
|
+
type: 'integer',
|
|
101
|
+
value: '<n>',
|
|
102
|
+
summary: 'Maximum organization candidates returned.',
|
|
103
|
+
constraints: { default: 50, minimum: 1, maximum: 100 },
|
|
104
|
+
});
|
|
105
|
+
const TRANSFER_OUT_FLAG = flag({
|
|
106
|
+
...FLAG_FIELDS,
|
|
107
|
+
name: 'out',
|
|
108
|
+
type: 'string',
|
|
109
|
+
value: '<reviewed-preview.json>',
|
|
110
|
+
summary: 'New private path for the reviewed preview artifact.',
|
|
111
|
+
required: true,
|
|
112
|
+
sensitive: true,
|
|
113
|
+
});
|
|
114
|
+
const TRANSFER_PREVIEW_FILE_FLAG = flag({
|
|
115
|
+
...FLAG_FIELDS,
|
|
116
|
+
name: 'preview-file',
|
|
117
|
+
type: 'string',
|
|
118
|
+
value: '<reviewed-preview.json>',
|
|
119
|
+
summary: 'Private reviewed billing transfer preview artifact.',
|
|
120
|
+
required: true,
|
|
121
|
+
sensitive: true,
|
|
122
|
+
});
|
|
123
|
+
const TRANSFER_IDEMPOTENCY_FLAG = flag({
|
|
124
|
+
...IDEMPOTENCY_FLAG,
|
|
125
|
+
constraints: { minLength: 8, maxLength: 256 },
|
|
126
|
+
});
|
|
127
|
+
const TRANSFER_YES_FLAG = flag({ ...YES_FLAG, required: true });
|
|
128
|
+
const TRANSFER_ADMIN_REASON_FLAG = flag({
|
|
129
|
+
...REASON_FLAG,
|
|
130
|
+
summary: 'Restricted administrative reason recorded with the transfer.',
|
|
131
|
+
sensitive: true,
|
|
132
|
+
constraints: { minLength: 1, maxLength: 256 },
|
|
133
|
+
});
|
|
65
134
|
function leaf(name, summary, options) {
|
|
66
135
|
return subcommand({
|
|
67
136
|
...SUBCOMMAND_FIELDS,
|
|
@@ -281,6 +350,50 @@ const MIGRATION = branch('migration', 'Run the super-admin-only legacy shadow-mi
|
|
|
281
350
|
],
|
|
282
351
|
}),
|
|
283
352
|
]);
|
|
353
|
+
const CUSTOMER_TRANSFER = branch('transfer', 'Move an owned organization to an eligible billing account.', [
|
|
354
|
+
leaf('candidates', 'List owned organizations with billing transfer status and eligibility.', {
|
|
355
|
+
...LEAF_FIELDS,
|
|
356
|
+
flags: [
|
|
357
|
+
TRANSFER_ACCOUNT_FLAG,
|
|
358
|
+
TRANSFER_QUERY_FLAG,
|
|
359
|
+
TRANSFER_CURSOR_FLAG,
|
|
360
|
+
TRANSFER_LIMIT_FLAG,
|
|
361
|
+
],
|
|
362
|
+
}),
|
|
363
|
+
leaf('preview', 'Preview an exact customer billing transfer without changing state.', {
|
|
364
|
+
arguments: [
|
|
365
|
+
argument({
|
|
366
|
+
...ARGUMENT_FIELDS,
|
|
367
|
+
name: 'org-slug',
|
|
368
|
+
type: 'string',
|
|
369
|
+
summary: 'Owned organization slug to transfer.',
|
|
370
|
+
required: true,
|
|
371
|
+
}),
|
|
372
|
+
],
|
|
373
|
+
flags: [TRANSFER_ACCOUNT_FLAG, TRANSFER_OUT_FLAG],
|
|
374
|
+
}),
|
|
375
|
+
leaf('apply', 'Apply an exact reviewed customer billing transfer.', {
|
|
376
|
+
...LEAF_FIELDS,
|
|
377
|
+
flags: [TRANSFER_PREVIEW_FILE_FLAG, TRANSFER_IDEMPOTENCY_FLAG, TRANSFER_YES_FLAG],
|
|
378
|
+
}),
|
|
379
|
+
]);
|
|
380
|
+
const ADMINISTRATION = branch('administration', 'Perform restricted super-admin billing operations on behalf of users.', [
|
|
381
|
+
branch('transfer', 'Move organization billing with explicit super-admin authority.', [
|
|
382
|
+
leaf('preview', 'Preview an exact administrative billing transfer.', {
|
|
383
|
+
...LEAF_FIELDS,
|
|
384
|
+
flags: [
|
|
385
|
+
TRANSFER_ORG_FLAG,
|
|
386
|
+
TRANSFER_ACCOUNT_FLAG,
|
|
387
|
+
TRANSFER_ADMIN_REASON_FLAG,
|
|
388
|
+
TRANSFER_OUT_FLAG,
|
|
389
|
+
],
|
|
390
|
+
}),
|
|
391
|
+
leaf('apply', 'Apply an exact reviewed administrative billing transfer.', {
|
|
392
|
+
...LEAF_FIELDS,
|
|
393
|
+
flags: [TRANSFER_PREVIEW_FILE_FLAG, TRANSFER_IDEMPOTENCY_FLAG, TRANSFER_YES_FLAG],
|
|
394
|
+
}),
|
|
395
|
+
]),
|
|
396
|
+
]);
|
|
284
397
|
export const BILLING_COMMAND = {
|
|
285
398
|
name: 'billing',
|
|
286
399
|
section: 'operate',
|
|
@@ -289,7 +402,7 @@ export const BILLING_COMMAND = {
|
|
|
289
402
|
arguments: [],
|
|
290
403
|
subcommands: [
|
|
291
404
|
ACCOUNTS,
|
|
292
|
-
branch('org', 'Inspect an organization’s effective billing
|
|
405
|
+
branch('org', 'Inspect or transfer an organization’s effective billing.', [
|
|
293
406
|
leaf('inspect', 'Inspect effective billing entitlements for one organization.', {
|
|
294
407
|
...LEAF_FIELDS,
|
|
295
408
|
arguments: [
|
|
@@ -302,7 +415,9 @@ export const BILLING_COMMAND = {
|
|
|
302
415
|
}),
|
|
303
416
|
],
|
|
304
417
|
}),
|
|
418
|
+
CUSTOMER_TRANSFER,
|
|
305
419
|
]),
|
|
420
|
+
ADMINISTRATION,
|
|
306
421
|
ENFORCEMENT,
|
|
307
422
|
METERING,
|
|
308
423
|
MIGRATION,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Shared manifest primitives: colors, https URLs, managed variable expressions, assistant origins,
|
|
4
|
+
* packaged asset references, and the server brand kit.
|
|
5
|
+
*
|
|
6
|
+
* Extracted verbatim from `schema.ts` so that file stays inside its size budget as the assistant
|
|
7
|
+
* surface grows. These are leaf validators with no dependency on tools, prompts, widgets, or the
|
|
8
|
+
* assistant block, which is what makes the split one-directional and free of an import cycle.
|
|
9
|
+
*/
|
|
10
|
+
const hexColorSchema = z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'must be a 6-digit hex color');
|
|
11
|
+
export const httpsUrlSchema = z.url().regex(/^https:\/\//, 'must use https');
|
|
12
|
+
const managedVariableExpressionPattern = /^\$\{env\.[A-Za-z0-9_]+\}$/;
|
|
13
|
+
export const managedVariableExpressionSchema = z
|
|
14
|
+
.string()
|
|
15
|
+
.regex(managedVariableExpressionPattern, 'must be an exact managed variable expression');
|
|
16
|
+
// HTTP is allowed only for loopback development origins (mirrors the host-pattern loopback
|
|
17
|
+
// exception used for connector/CSP origins); production embedding origins stay https-only.
|
|
18
|
+
const assistantOriginOrVariablePattern = /^(?:\$\{env\.[A-Za-z0-9_]+\}|https:\/\/[^/?#@\\]+|http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?)$/;
|
|
19
|
+
function isCanonicalAssistantOrigin(value) {
|
|
20
|
+
try {
|
|
21
|
+
return new URL(value).origin === value;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const assistantOriginOrVariableSchema = z
|
|
28
|
+
.string()
|
|
29
|
+
.regex(assistantOriginOrVariablePattern, 'must be a canonical bare origin using https (http is allowed only for localhost/127.0.0.1/[::1] loopback development origins)')
|
|
30
|
+
.refine((value) => managedVariableExpressionPattern.test(value) || isCanonicalAssistantOrigin(value), 'must be a canonical bare origin');
|
|
31
|
+
const packagedAssetSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
kind: z.literal('asset'),
|
|
34
|
+
sourcePath: z.string().min(1),
|
|
35
|
+
logicalId: z.string().min(1),
|
|
36
|
+
})
|
|
37
|
+
.strict();
|
|
38
|
+
const brandAssetSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
uri: z.union([httpsUrlSchema, packagedAssetSchema]),
|
|
41
|
+
darkUri: z.union([httpsUrlSchema, packagedAssetSchema]).optional(),
|
|
42
|
+
alt: z.string().trim().min(1),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
const brandThemeTokensSchema = z
|
|
46
|
+
.object({
|
|
47
|
+
surface: hexColorSchema.optional(),
|
|
48
|
+
surfaceRaised: hexColorSchema.optional(),
|
|
49
|
+
surfaceMuted: hexColorSchema.optional(),
|
|
50
|
+
text: hexColorSchema.optional(),
|
|
51
|
+
textMuted: hexColorSchema.optional(),
|
|
52
|
+
accent: hexColorSchema.optional(),
|
|
53
|
+
accentText: hexColorSchema.optional(),
|
|
54
|
+
link: hexColorSchema.optional(),
|
|
55
|
+
border: hexColorSchema.optional(),
|
|
56
|
+
borderStrong: hexColorSchema.optional(),
|
|
57
|
+
focus: hexColorSchema.optional(),
|
|
58
|
+
success: hexColorSchema.optional(),
|
|
59
|
+
warning: hexColorSchema.optional(),
|
|
60
|
+
danger: hexColorSchema.optional(),
|
|
61
|
+
code: hexColorSchema.optional(),
|
|
62
|
+
})
|
|
63
|
+
.strict();
|
|
64
|
+
export const serverBrandingSchema = z
|
|
65
|
+
.object({
|
|
66
|
+
name: z.string().trim().min(1).optional(),
|
|
67
|
+
accent: hexColorSchema.optional(),
|
|
68
|
+
surface: hexColorSchema.optional(),
|
|
69
|
+
surfaceDark: hexColorSchema.optional(),
|
|
70
|
+
logo: brandAssetSchema.optional(),
|
|
71
|
+
mark: brandAssetSchema.optional(),
|
|
72
|
+
avatar: brandAssetSchema.optional(),
|
|
73
|
+
theme: z
|
|
74
|
+
.object({
|
|
75
|
+
light: brandThemeTokensSchema.optional(),
|
|
76
|
+
dark: brandThemeTokensSchema.optional(),
|
|
77
|
+
})
|
|
78
|
+
.strict()
|
|
79
|
+
.optional(),
|
|
80
|
+
radius: z.enum(['none', 'sm', 'md', 'lg']).optional(),
|
|
81
|
+
density: z.enum(['compact', 'comfortable']).optional(),
|
|
82
|
+
typography: z.enum(['system', 'serif', 'mono']).optional(),
|
|
83
|
+
colorScheme: z.enum(['auto', 'light', 'dark']).optional(),
|
|
84
|
+
})
|
|
85
|
+
.strict();
|
|
86
|
+
//# sourceMappingURL=branding-schema.js.map
|