primitive-admin 1.1.0-alpha.82 → 1.1.0-alpha.83
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/src/commands/documents.js +333 -42
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/functions.js +302 -120
- package/dist/src/commands/functions.js.map +1 -1
- package/dist/src/commands/locks.js +15 -1
- package/dist/src/commands/locks.js.map +1 -1
- package/dist/src/commands/sync.d.ts +50 -0
- package/dist/src/commands/sync.js +560 -312
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +75 -2
- package/dist/src/lib/api-client.js +122 -7
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/config-object-descriptor.js +5 -5
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
- package/dist/src/lib/document-ingest-artifact.js +505 -0
- package/dist/src/lib/document-ingest-artifact.js.map +1 -0
- package/dist/src/lib/document-ingest-input.d.ts +51 -0
- package/dist/src/lib/document-ingest-input.js +132 -0
- package/dist/src/lib/document-ingest-input.js.map +1 -0
- package/dist/src/lib/document-ingest.d.ts +78 -0
- package/dist/src/lib/document-ingest.js +231 -0
- package/dist/src/lib/document-ingest.js.map +1 -0
- package/dist/src/lib/function-bundle.d.ts +6 -0
- package/dist/src/lib/function-bundle.js +8 -0
- package/dist/src/lib/function-bundle.js.map +1 -1
- package/dist/src/lib/function-collect.d.ts +1 -1
- package/dist/src/lib/function-collect.js +16 -0
- package/dist/src/lib/function-collect.js.map +1 -1
- package/dist/src/lib/function-db-types.d.ts +4 -0
- package/dist/src/lib/function-db-types.js +31 -2
- package/dist/src/lib/function-db-types.js.map +1 -1
- package/dist/src/lib/function-document-types.d.ts +83 -0
- package/dist/src/lib/function-document-types.js +133 -0
- package/dist/src/lib/function-document-types.js.map +1 -1
- package/dist/src/lib/function-grants-preflight.js +5 -0
- package/dist/src/lib/function-grants-preflight.js.map +1 -1
- package/dist/src/lib/function-log-lines.d.ts +76 -0
- package/dist/src/lib/function-log-lines.js +160 -0
- package/dist/src/lib/function-log-lines.js.map +1 -0
- package/dist/src/lib/function-log-row.d.ts +29 -0
- package/dist/src/lib/function-log-row.js +73 -0
- package/dist/src/lib/function-log-row.js.map +1 -0
- package/dist/src/lib/function-run.d.ts +53 -35
- package/dist/src/lib/function-run.js +55 -44
- package/dist/src/lib/function-run.js.map +1 -1
- package/dist/src/lib/function-schema-codegen.d.ts +6 -9
- package/dist/src/lib/function-schema-codegen.js +22 -35
- package/dist/src/lib/function-schema-codegen.js.map +1 -1
- package/dist/src/lib/function-sync.d.ts +226 -35
- package/dist/src/lib/function-sync.js +731 -99
- package/dist/src/lib/function-sync.js.map +1 -1
- package/dist/src/lib/function-versions.d.ts +122 -0
- package/dist/src/lib/function-versions.js +182 -0
- package/dist/src/lib/function-versions.js.map +1 -0
- package/dist/src/lib/generated-config-surfaces.d.ts +390 -146
- package/dist/src/lib/generated-config-surfaces.js +1207 -401
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/log-inspection.d.ts +2 -0
- package/dist/src/lib/log-inspection.js +4 -0
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/output.d.ts +15 -0
- package/dist/src/lib/output.js +28 -0
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
- package/dist/src/lib/snapshot-audit-source.js +58 -0
- package/dist/src/lib/snapshot-audit-source.js.map +1 -0
- package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
- package/dist/src/lib/snapshot-audit-store.js +196 -0
- package/dist/src/lib/snapshot-audit-store.js.map +1 -0
- package/dist/src/lib/snapshot-audit.d.ts +207 -0
- package/dist/src/lib/snapshot-audit.js +431 -0
- package/dist/src/lib/snapshot-audit.js.map +1 -0
- package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
- package/dist/src/lib/snapshot-build-rows.js +87 -0
- package/dist/src/lib/snapshot-build-rows.js.map +1 -0
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +23 -16
- package/dist/src/lib/swift-codegen/functionGenerator.js +34 -33
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
- package/dist/src/lib/sync-selectors.d.ts +45 -2
- package/dist/src/lib/sync-selectors.js +66 -5
- package/dist/src/lib/sync-selectors.js.map +1 -1
- package/package.json +2 -2
|
@@ -21,20 +21,22 @@ import { join, relative as relativePath } from "path";
|
|
|
21
21
|
import { ApiClient } from "../lib/api-client.js";
|
|
22
22
|
import { resolveAppId } from "../lib/config.js";
|
|
23
23
|
import { resolveSyncDir } from "../lib/sync-paths.js";
|
|
24
|
-
import { activeVersionMode } from "../lib/function-sync.js";
|
|
25
24
|
import { FunctionSchemaCodegenError, functionSchemaSources, } from "../lib/function-schema-codegen.js";
|
|
26
25
|
import { functionTypeWiringProblems, staleFunctionTypeArtifacts, writeFunctionTypeArtifacts, } from "../lib/function-db-types.js";
|
|
27
26
|
import { generateFunctionSwiftTypes } from "../lib/swift-codegen/functionGenerator.js";
|
|
28
27
|
import { SwiftCodegenError } from "../lib/swift-codegen/schemaToSwift.js";
|
|
29
28
|
import { success, error, info, warn, keyValue, result as printResult, formatTable, formatDate, formatStatus, json, jsonLine, divider, } from "../lib/output.js";
|
|
30
29
|
import { confirmPrompt } from "../lib/confirm-prompt.js";
|
|
30
|
+
import { FUNCTION_LOG_COLUMNS, functionLogCells, functionLogLine, } from "../lib/function-log-row.js";
|
|
31
|
+
import { activatedLine, activationOutput, activationPrompt, activationRefusalLine, functionVersionRows, newerVersionLine, versionLine, } from "../lib/function-versions.js";
|
|
31
32
|
import { parseStatusFilter } from "../lib/object-status-filter.js";
|
|
32
33
|
import { renderStepRunTable } from "../lib/step-run-table.js";
|
|
34
|
+
import { renderRunLogs } from "../lib/function-log-lines.js";
|
|
33
35
|
import { buildWorkflowStepEnvelope } from "../lib/log-inspection.js";
|
|
34
36
|
import { followLoop } from "../lib/watch.js";
|
|
35
37
|
import { buildLogFollowSource } from "../lib/function-log-tail.js";
|
|
36
38
|
import { toFunctionLogInspectionRow } from "../lib/log-inspection.js";
|
|
37
|
-
import { decidePageContinuation, describeIdentity, exitCodeForInvokeStatus, exitCodeForRunStatus, invokeIdLines, logsFlagConflict,
|
|
39
|
+
import { decidePageContinuation, describeIdentity, exitCodeForInvokeStatus, exitCodeForRunStatus, invokeIdLines, logsFlagConflict, refusalLines, renderIdLines, resolveRunFlags, runRuntimeCell, runSliceCells, runtimeForExecutionMode, resumeCommand, startIdLines, userTokenName, waitDelayMs, withMintedToken, EXIT_INTERRUPTED, EXIT_TIMED_OUT, USER_TOKEN_TTL, WAIT_DEFAULT_TIMEOUT_SECONDS, } from "../lib/function-run.js";
|
|
38
40
|
/**
|
|
39
41
|
* How far back one `--follow` tick will page to meet its high-water mark.
|
|
40
42
|
*
|
|
@@ -48,25 +50,26 @@ import { decidePageContinuation, describeIdentity, exitCodeForInvokeStatus, exit
|
|
|
48
50
|
*/
|
|
49
51
|
const FOLLOW_MAX_PAGES_PER_TICK = 10;
|
|
50
52
|
/**
|
|
51
|
-
*
|
|
53
|
+
* Which cell each shared invocation column reads, and how it is rendered.
|
|
52
54
|
*
|
|
53
|
-
* The
|
|
54
|
-
*
|
|
55
|
+
* The ORDER lives in `FUNCTION_LOG_COLUMNS` (#3462); this is only the mapping
|
|
56
|
+
* from a header to the cell `functionLogCells` builds for it, so adding a
|
|
57
|
+
* column is one edit in each place rather than three copies of the order.
|
|
55
58
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
59
|
+
const LOG_COLUMN_KEYS = {
|
|
60
|
+
TIME: "startedAt",
|
|
61
|
+
STATUS: "status",
|
|
62
|
+
TRIGGER: "trigger",
|
|
63
|
+
RUNTIME: "runtime",
|
|
64
|
+
VERSION: "configId",
|
|
65
|
+
"RUN/INVOCATION ID": "runOrInvocation",
|
|
66
|
+
CODE: "errorCode",
|
|
67
|
+
ERROR: "firstError",
|
|
68
|
+
};
|
|
69
|
+
const LOG_COLUMN_FORMATS = {
|
|
70
|
+
TIME: formatDate,
|
|
71
|
+
STATUS: formatStatus,
|
|
72
|
+
};
|
|
70
73
|
export function registerFunctionsCommands(program) {
|
|
71
74
|
const functions = program
|
|
72
75
|
.command("functions")
|
|
@@ -145,18 +148,56 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
145
148
|
// The versions are append-only and nameless; `activeConfigId` is the
|
|
146
149
|
// only thing that says which one runs, so a listing without it would
|
|
147
150
|
// report the newest rather than the live one.
|
|
151
|
+
//
|
|
152
|
+
// #3462 — a POINT READ, not a drained listing scanned for the id. Past
|
|
153
|
+
// 25 versions the listing is a page and the active version may not be
|
|
154
|
+
// on it at all; one read by id is both cheaper and correct.
|
|
148
155
|
let active = null;
|
|
149
156
|
if (record?.activeConfigId) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
// #3455 — this single read is also what carries
|
|
158
|
+
// `documentSchemaModels`: the models this version can seed are
|
|
159
|
+
// derived from the schema INSIDE the envelope rather than stored on
|
|
160
|
+
// the row, so the LISTING (one row per version, no R2 read) cannot
|
|
161
|
+
// carry them and only the point read can. Best-effort — an older
|
|
162
|
+
// server sends no such field, and the line then reads exactly as it
|
|
163
|
+
// does for a version pushed without a schema.
|
|
164
|
+
try {
|
|
165
|
+
active = await client.getFunctionConfig(resolvedAppId, functionId, String(record.activeConfigId));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
active = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// #3462 — one row of the listing answers "is the active version the
|
|
172
|
+
// newest", which is what tells an operator this function is rolled back.
|
|
173
|
+
let newest = null;
|
|
174
|
+
try {
|
|
175
|
+
const page = await client.listFunctionConfigs(resolvedAppId, functionId, {
|
|
176
|
+
limit: 1,
|
|
177
|
+
});
|
|
178
|
+
newest = (page.items || [])[0] ?? null;
|
|
153
179
|
}
|
|
180
|
+
catch {
|
|
181
|
+
newest = null;
|
|
182
|
+
}
|
|
183
|
+
const newer = newerVersionLine(newest, record?.activeConfigId, formatDate);
|
|
154
184
|
if (options.json) {
|
|
155
|
-
json({
|
|
185
|
+
json({
|
|
186
|
+
...record,
|
|
187
|
+
activeConfig: active,
|
|
188
|
+
...(newer ? { newerVersion: newest } : {}),
|
|
189
|
+
});
|
|
156
190
|
return;
|
|
157
191
|
}
|
|
158
192
|
printResult("Function ID", record.functionId);
|
|
159
193
|
printResult("Key", record.functionKey);
|
|
194
|
+
// #3462 — which code this function is running, beside its identity. The
|
|
195
|
+
// version was only reachable by reading the section below, or by asking
|
|
196
|
+
// the API; an operator answering "what is deployed" reads this line.
|
|
197
|
+
printResult("Version", versionLine({
|
|
198
|
+
activeConfigId: record.activeConfigId,
|
|
199
|
+
contentHash: active?.contentHash,
|
|
200
|
+
}));
|
|
160
201
|
printResult("Description", record.description || "-");
|
|
161
202
|
printResult("Status", formatStatus(record.status));
|
|
162
203
|
printResult("Access", record.access || "-");
|
|
@@ -171,11 +212,9 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
171
212
|
printResult(" Config ID", active.configId);
|
|
172
213
|
printResult(" Entry", active.entry);
|
|
173
214
|
printResult(" Content Hash", active.contentHash || "-");
|
|
174
|
-
// #
|
|
175
|
-
// `
|
|
176
|
-
//
|
|
177
|
-
const activeMode = activeVersionMode(active);
|
|
178
|
-
printResult(" Mode", activeMode === "any" ? "any (invoke or start)" : activeMode);
|
|
215
|
+
// #3482 — no Mode line. The config says nothing about how a
|
|
216
|
+
// function runs: `invoke` runs it inside the request, `start` runs
|
|
217
|
+
// it as a task, and every function takes both.
|
|
179
218
|
printResult(" Limits", active.limits || "-");
|
|
180
219
|
// #3182 — what this version declared: since #3279 the egress
|
|
181
220
|
// allowlist, the secrets that may enter the sandbox and the
|
|
@@ -185,6 +224,28 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
185
224
|
? active.capabilities
|
|
186
225
|
: [];
|
|
187
226
|
printResult(" Capabilities", capabilities.length > 0 ? capabilities.join(", ") : "-");
|
|
227
|
+
// #3462 — who put this version live and when, from either door (a
|
|
228
|
+
// push or `functions activate`). A header written before that change
|
|
229
|
+
// carries neither field and reads `unknown` rather than naming an
|
|
230
|
+
// actor nobody recorded.
|
|
231
|
+
printResult(" Activated", activatedLine(record, formatDate));
|
|
232
|
+
if (newer) {
|
|
233
|
+
printResult(" Newer version", newer);
|
|
234
|
+
}
|
|
235
|
+
// #3455 — the document models this version can SEED on a first
|
|
236
|
+
// write. An operator asking "why did the first write not seed
|
|
237
|
+
// `institution`" gets the answer here: either the model is in this
|
|
238
|
+
// list, or the version was pushed without a document schema at all.
|
|
239
|
+
// A server that predates the field sends nothing, which reads the
|
|
240
|
+
// same way as a version pushed without one.
|
|
241
|
+
const documentSchemaModels = Array.isArray(active.documentSchemaModels)
|
|
242
|
+
? active.documentSchemaModels
|
|
243
|
+
: null;
|
|
244
|
+
printResult(" Document schema", documentSchemaModels === null
|
|
245
|
+
? "none — this version was pushed without one"
|
|
246
|
+
: documentSchemaModels.length > 0
|
|
247
|
+
? documentSchemaModels.join(", ")
|
|
248
|
+
: "declares no models");
|
|
188
249
|
}
|
|
189
250
|
// #3181 — the ONE surface a function's triggers are visible on. The
|
|
190
251
|
// hidden `AppWebhook` and `CronTrigger` rows are omitted from
|
|
@@ -197,17 +258,11 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
197
258
|
const cronEntries = Array.isArray(triggers.cron)
|
|
198
259
|
? triggers.cron
|
|
199
260
|
: [];
|
|
200
|
-
// #
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
const databaseEntries = Array.isArray(triggers.databases)
|
|
206
|
-
? triggers.databases
|
|
207
|
-
: [];
|
|
208
|
-
if (!triggers.webhook &&
|
|
209
|
-
cronEntries.length === 0 &&
|
|
210
|
-
databaseEntries.length === 0) {
|
|
261
|
+
// #3483 — an OLDER server still answers a `databases` key, and this
|
|
262
|
+
// command ignores it rather than branching on it: the trigger it
|
|
263
|
+
// described was removed, so a section about it would tell an operator
|
|
264
|
+
// that a write runs this function when nothing does.
|
|
265
|
+
if (!triggers.webhook && cronEntries.length === 0) {
|
|
211
266
|
printResult(" Triggers", "none — this function is invoked over HTTP only");
|
|
212
267
|
}
|
|
213
268
|
if (triggers.webhook) {
|
|
@@ -228,7 +283,6 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
228
283
|
// #3454 — which runner this schedule's fires use. Always one of
|
|
229
284
|
// the two words: an `any` function's entry names it, and a
|
|
230
285
|
// locked one's takes its lock.
|
|
231
|
-
{ header: "RUNNER", key: "runner" },
|
|
232
286
|
{ header: "TZ", key: "timezone" },
|
|
233
287
|
{ header: "STATUS", key: "status", format: formatStatus },
|
|
234
288
|
{ header: "NEXT FIRE", key: "nextFireAt", format: formatDate },
|
|
@@ -236,15 +290,6 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
236
290
|
{ header: "LAST RUN", key: "lastTriggeredRunId" },
|
|
237
291
|
]));
|
|
238
292
|
}
|
|
239
|
-
if (databaseEntries.length > 0) {
|
|
240
|
-
printResult(" Database changes", "a committed write to one of these types runs this function");
|
|
241
|
-
// A fire carries the rows of every change of the type (the intent's
|
|
242
|
-
// 2026-09-09 decision), so the row says which type and no more —
|
|
243
|
-
// the READ GRANTS column retired with the grant grammar (#3279).
|
|
244
|
-
console.log(formatTable(databaseEntries, [
|
|
245
|
-
{ header: "DATABASE TYPE", key: "databaseType" },
|
|
246
|
-
]));
|
|
247
|
-
}
|
|
248
293
|
// #3187 / #3279 — the ACTIVE version's manifest: what it registered
|
|
249
294
|
// and what it TOUCHES, from the push-time collection. Documentation,
|
|
250
295
|
// never authorization — function code acts as the system, and this
|
|
@@ -307,6 +352,124 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
307
352
|
process.exit(1);
|
|
308
353
|
}
|
|
309
354
|
});
|
|
355
|
+
// ── #3462: a function's versions, and pointing it back at one ─────────
|
|
356
|
+
//
|
|
357
|
+
// Every changing push mints an immutable version and repoints the function at
|
|
358
|
+
// it. That was already true and already stored — and entirely invisible: an
|
|
359
|
+
// operator could not list a function's versions, and a bad push was undone by
|
|
360
|
+
// editing the tree and pushing again, with `disable` as the only immediate
|
|
361
|
+
// stop.
|
|
362
|
+
//
|
|
363
|
+
// Both are OPERATIONAL verbs on a configuration group in `docs/cli-design.md`'s
|
|
364
|
+
// sense: they author nothing. A function's versions are nameless, immutable
|
|
365
|
+
// and not in TOML — unlike a prompt's `[[configs]]` blocks, whose `active`
|
|
366
|
+
// marker is why `prompts configs activate` was retired — so there is no TOML
|
|
367
|
+
// spelling of activation to prefer to a verb.
|
|
368
|
+
functions
|
|
369
|
+
.command("configs")
|
|
370
|
+
.description("List a server function's versions, newest first")
|
|
371
|
+
.argument("<function-id>", "Function ID")
|
|
372
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
373
|
+
.option("--limit <n>", "Rows per page (default 25, max 100)")
|
|
374
|
+
.option("--cursor <cursor>", "Continue from a previous page")
|
|
375
|
+
.option("--json", "Output as JSON")
|
|
376
|
+
.action(async (functionId, options) => {
|
|
377
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
378
|
+
const client = new ApiClient();
|
|
379
|
+
try {
|
|
380
|
+
// Always a `limit`, so this pages. The server's no-parameter form
|
|
381
|
+
// drains every version and exists for a CLI installed before paging
|
|
382
|
+
// did — this one is not it.
|
|
383
|
+
const { items, nextCursor } = await client.listFunctionConfigs(resolvedAppId, functionId, {
|
|
384
|
+
limit: options.limit ? Number(options.limit) : 25,
|
|
385
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
386
|
+
});
|
|
387
|
+
if (options.json) {
|
|
388
|
+
// Full hashes, not the table's twelve characters: a consumer
|
|
389
|
+
// comparing versions needs the value the server stored.
|
|
390
|
+
json({ items, hasMore: !!nextCursor, ...(nextCursor ? { nextCursor } : {}) });
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (!items || items.length === 0) {
|
|
394
|
+
info("No versions pushed yet.");
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
console.log(formatTable(functionVersionRows(items), [
|
|
398
|
+
{ header: "CONFIG ID", key: "configId" },
|
|
399
|
+
{ header: "PUSHED", key: "createdAt", format: formatDate },
|
|
400
|
+
{ header: "ACTIVE", key: "active" },
|
|
401
|
+
{ header: "CONTENT", key: "content" },
|
|
402
|
+
{ header: "ENVELOPE", key: "envelope" },
|
|
403
|
+
{ header: "TRIGGERS", key: "triggers" },
|
|
404
|
+
{ header: "BY", key: "by" },
|
|
405
|
+
]));
|
|
406
|
+
if (nextCursor) {
|
|
407
|
+
info(`More versions: --cursor ${nextCursor}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
error(err.message);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
functions
|
|
416
|
+
.command("activate")
|
|
417
|
+
.description("Point a server function at one of its own existing versions (roll back or forward)")
|
|
418
|
+
.argument("<function-id>", "Function ID")
|
|
419
|
+
.argument("<config-id>", "The version to activate (`functions configs`)")
|
|
420
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
421
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
422
|
+
.option("--json", "Output as JSON")
|
|
423
|
+
.addHelpText("after", `
|
|
424
|
+
What activating a version does, and does not:
|
|
425
|
+
Request calls and trigger fires use it from the NEXT call, under that
|
|
426
|
+
version's own capabilities, limits and triggers.
|
|
427
|
+
A task run already in flight finishes on the version it started on — the
|
|
428
|
+
content hash pins its code.
|
|
429
|
+
No version is created. \`config push\` of the tree is still what makes the
|
|
430
|
+
tree's code the newest version.
|
|
431
|
+
`)
|
|
432
|
+
.action(async (functionId, configId, options) => {
|
|
433
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
434
|
+
if (!options.yes) {
|
|
435
|
+
let confirmed;
|
|
436
|
+
try {
|
|
437
|
+
confirmed = await confirmPrompt(activationPrompt(String(functionId), String(configId)));
|
|
438
|
+
}
|
|
439
|
+
catch (err) {
|
|
440
|
+
error(err.message);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
if (!confirmed) {
|
|
444
|
+
info("Cancelled.");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const client = new ApiClient();
|
|
449
|
+
try {
|
|
450
|
+
const answer = await client.activateFunctionConfig(resolvedAppId, functionId, configId);
|
|
451
|
+
if (options.json) {
|
|
452
|
+
json(answer);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const rendered = activationOutput(answer, formatDate);
|
|
456
|
+
if (!rendered.changed) {
|
|
457
|
+
info(rendered.headline);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
success(rendered.headline);
|
|
461
|
+
for (const [label, value] of rendered.lines)
|
|
462
|
+
printResult(label, value);
|
|
463
|
+
}
|
|
464
|
+
catch (err) {
|
|
465
|
+
// The server's own sentence and code: an archived function, an archived
|
|
466
|
+
// version, a held delete lease and a pointer another writer moved each
|
|
467
|
+
// owe the operator a different next move, and only the code says which
|
|
468
|
+
// one without reading English.
|
|
469
|
+
error(activationRefusalLine(err));
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
310
473
|
// #3181 — a trigger-fired invocation writes a run row, and a function run has
|
|
311
474
|
// no `workflowId`, so `primitive workflows runs` cannot see it. This verb is
|
|
312
475
|
// the only way to inspect what a webhook or a schedule actually did
|
|
@@ -327,8 +490,17 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
327
490
|
...(options.limit ? { limit: Number(options.limit) } : {}),
|
|
328
491
|
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
329
492
|
});
|
|
493
|
+
// #3482, C-6 — the runtime this run used, derived BEFORE the output is
|
|
494
|
+
// chosen so the table and `--json` say the same thing about the same
|
|
495
|
+
// row. `--json` takes the word or `null`, never the table's blank: a
|
|
496
|
+
// reader parsing this needs "no runtime" to be a value rather than an
|
|
497
|
+
// empty string that sorts beside real ones.
|
|
498
|
+
const withRuntime = (items ?? []).map((run) => ({
|
|
499
|
+
...run,
|
|
500
|
+
runtime: runtimeForExecutionMode(run?.executionMode),
|
|
501
|
+
}));
|
|
330
502
|
if (options.json) {
|
|
331
|
-
json({ items, nextCursor });
|
|
503
|
+
json({ items: withRuntime, nextCursor });
|
|
332
504
|
return;
|
|
333
505
|
}
|
|
334
506
|
if (!items || items.length === 0) {
|
|
@@ -336,18 +508,29 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
336
508
|
return;
|
|
337
509
|
}
|
|
338
510
|
console.log(formatTable(
|
|
339
|
-
// #3381 — flatten the slice's
|
|
340
|
-
//
|
|
341
|
-
//
|
|
511
|
+
// #3381, #3566 — flatten the slice record's two counts onto the
|
|
512
|
+
// row for the REFRESHES and RESETS columns. One helper decides
|
|
513
|
+
// both, so a row with no record gets a blank cell in each and a
|
|
514
|
+
// record with no count gets `0`.
|
|
342
515
|
items.map((run) => ({
|
|
343
516
|
...run,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
517
|
+
// #3482 — which RUNTIME this run used, from the row's own
|
|
518
|
+
// `executionMode`. One pure mapping, so the word an operator
|
|
519
|
+
// reads here is the word `ctx.runtime` gave the code.
|
|
520
|
+
runtime: runRuntimeCell(run),
|
|
521
|
+
...runSliceCells(run),
|
|
347
522
|
})), [
|
|
348
523
|
{ header: "RUN ID", key: "runId" },
|
|
349
524
|
{ header: "STATUS", key: "status", format: formatStatus },
|
|
350
525
|
{ header: "FIRED BY", key: "initiatorKind" },
|
|
526
|
+
// #3482 — the runtime this run used: `request` for a webhook
|
|
527
|
+
// delivery or a `workflow.call` leaf, `task` for everything the
|
|
528
|
+
// engine ran. Blank for a DSL row, which is not a function run.
|
|
529
|
+
{ header: "RUNTIME", key: "runtime" },
|
|
530
|
+
// #3462 — WHICH version this run executed. The row has carried
|
|
531
|
+
// `configId` since #3181; nothing showed it, so an operator
|
|
532
|
+
// reading a failed run could not tell which code failed.
|
|
533
|
+
{ header: "VERSION", key: "configId" },
|
|
351
534
|
// #3280 — FIRED BY already said `function`; this is the half that
|
|
352
535
|
// makes it actionable, so a failed page leads back to the
|
|
353
536
|
// orchestrator that started it. Blank for a root.
|
|
@@ -355,6 +538,11 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
355
538
|
// #3381 — how many times a task slice's token was refreshed
|
|
356
539
|
// through the gateway. Blank for a request run.
|
|
357
540
|
{ header: "REFRESHES", key: "refreshes" },
|
|
541
|
+
// #3566 — how many of this run's slices a platform deploy (or an
|
|
542
|
+
// isolate eviction) tore down. The engine re-runs the interrupted
|
|
543
|
+
// step, so a run that reset and then COMPLETED carries no failure
|
|
544
|
+
// and no error code: this cell is the only place it shows.
|
|
545
|
+
{ header: "RESETS", key: "resets" },
|
|
358
546
|
{ header: "STARTED", key: "startedAt", format: formatDate },
|
|
359
547
|
{ header: "ENDED", key: "endedAt", format: formatDate },
|
|
360
548
|
{ header: "CODE", key: "errorCode" },
|
|
@@ -529,23 +717,13 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
529
717
|
json(toFunctionLogInspectionRow(record));
|
|
530
718
|
return;
|
|
531
719
|
}
|
|
532
|
-
console.log(formatTable([
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
firstError: firstErrorLine(record),
|
|
540
|
-
},
|
|
541
|
-
], [
|
|
542
|
-
{ header: "TIME", key: "startedAt", format: formatDate },
|
|
543
|
-
{ header: "STATUS", key: "status", format: formatStatus },
|
|
544
|
-
{ header: "TRIGGER", key: "trigger" },
|
|
545
|
-
{ header: "RUN/INVOCATION ID", key: "runOrInvocation" },
|
|
546
|
-
{ header: "CODE", key: "errorCode" },
|
|
547
|
-
{ header: "ERROR", key: "firstError" },
|
|
548
|
-
]));
|
|
720
|
+
console.log(formatTable([functionLogCells(record)], FUNCTION_LOG_COLUMNS.map((header) => ({
|
|
721
|
+
header,
|
|
722
|
+
key: LOG_COLUMN_KEYS[header],
|
|
723
|
+
...(LOG_COLUMN_FORMATS[header]
|
|
724
|
+
? { format: LOG_COLUMN_FORMATS[header] }
|
|
725
|
+
: {}),
|
|
726
|
+
}))));
|
|
549
727
|
return;
|
|
550
728
|
}
|
|
551
729
|
if (options.follow) {
|
|
@@ -588,7 +766,7 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
588
766
|
return;
|
|
589
767
|
}
|
|
590
768
|
for (const row of items)
|
|
591
|
-
console.log(
|
|
769
|
+
console.log(functionLogLine(row));
|
|
592
770
|
},
|
|
593
771
|
});
|
|
594
772
|
}
|
|
@@ -647,23 +825,39 @@ Configuration lives in functions/<key>.toml, and the code beside it:
|
|
|
647
825
|
info(`More logs: --cursor ${exhaustedCursor}`);
|
|
648
826
|
return;
|
|
649
827
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
//
|
|
655
|
-
//
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
828
|
+
if (options.run) {
|
|
829
|
+
// #3453 — reading ONE run, which is a different question from
|
|
830
|
+
// listing a function's invocations. A table of one row per record
|
|
831
|
+
// answers "which invocations were there"; what an operator opened
|
|
832
|
+
// `--run` for is what the run PRINTED, and until now that was only
|
|
833
|
+
// reachable through `--json`. So: the step trace first, then each
|
|
834
|
+
// record oldest first with its own lines under it.
|
|
835
|
+
let trace = null;
|
|
836
|
+
let traceError = null;
|
|
837
|
+
try {
|
|
838
|
+
const read = await client.getFunctionStepRuns(resolvedAppId, functionId, String(options.run));
|
|
839
|
+
trace = read?.items ?? [];
|
|
840
|
+
}
|
|
841
|
+
catch (traceErr) {
|
|
842
|
+
traceError = traceErr?.message ?? String(traceErr);
|
|
843
|
+
}
|
|
844
|
+
const rendered = renderRunLogs({ records: items, trace, traceError });
|
|
845
|
+
if (rendered.warning)
|
|
846
|
+
warn(rendered.warning);
|
|
847
|
+
for (const line of rendered.lines)
|
|
848
|
+
console.log(line);
|
|
849
|
+
if (nextCursor) {
|
|
850
|
+
info(`More logs: --cursor ${nextCursor}`);
|
|
851
|
+
}
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
console.log(formatTable(items.map((row) => functionLogCells(row)), FUNCTION_LOG_COLUMNS.map((header) => ({
|
|
855
|
+
header,
|
|
856
|
+
key: LOG_COLUMN_KEYS[header],
|
|
857
|
+
...(LOG_COLUMN_FORMATS[header]
|
|
858
|
+
? { format: LOG_COLUMN_FORMATS[header] }
|
|
859
|
+
: {}),
|
|
860
|
+
}))));
|
|
667
861
|
if (nextCursor) {
|
|
668
862
|
info(`More logs: --cursor ${nextCursor}`);
|
|
669
863
|
}
|
|
@@ -971,12 +1165,17 @@ Reclaiming the key means hard-deleting the row:
|
|
|
971
1165
|
// Every decision these verbs make is in `cli/src/lib/function-run.ts`, where
|
|
972
1166
|
// it is pinned without a server; what is here is the I/O and the rendering.
|
|
973
1167
|
/**
|
|
974
|
-
* Key → the function's id
|
|
1168
|
+
* Key → the function's id.
|
|
975
1169
|
*
|
|
976
1170
|
* `invoke` and `start` take the KEY, because that is the public route's
|
|
977
1171
|
* argument and what a developer wrote in their TOML — while `runs`, `logs`
|
|
978
1172
|
* and `steps` keep the function id they have always taken. So the key has to
|
|
979
1173
|
* be resolved, and the drained admin listing is what `functions list` reads.
|
|
1174
|
+
*
|
|
1175
|
+
* #3482 — the ACTIVE VERSION is no longer read. It was point-read for one
|
|
1176
|
+
* reason: the mode pre-check, which refused the wrong verb before a round
|
|
1177
|
+
* trip. There is no wrong verb any more — the caller picks the runtime and
|
|
1178
|
+
* every function takes both — so the read and the pre-check go together.
|
|
980
1179
|
*/
|
|
981
1180
|
async function resolveFunctionByKey(client, appId, functionKey) {
|
|
982
1181
|
const { items } = await client.listFunctions(appId);
|
|
@@ -986,22 +1185,7 @@ Reclaiming the key means hard-deleting the row:
|
|
|
986
1185
|
throw new Error(`No function with key '${functionKey}' in this app. ` +
|
|
987
1186
|
`List them with 'primitive functions list'.`);
|
|
988
1187
|
}
|
|
989
|
-
|
|
990
|
-
// A function with no pushed version has no mode to disagree with, and the
|
|
991
|
-
// server's `FUNCTION_NOT_PUSHED` is the honest answer (edge 30).
|
|
992
|
-
let mode = null;
|
|
993
|
-
try {
|
|
994
|
-
const { items: configs } = await client.listFunctionConfigs(appId, functionId);
|
|
995
|
-
const active = (configs || []).find((config) => String(config.configId) === String(match.activeConfigId));
|
|
996
|
-
if (active)
|
|
997
|
-
mode = activeVersionMode(active);
|
|
998
|
-
}
|
|
999
|
-
catch {
|
|
1000
|
-
// A version list this operator cannot read is not a reason to refuse the
|
|
1001
|
-
// call: the pre-check is a courtesy and the server is the authority.
|
|
1002
|
-
mode = null;
|
|
1003
|
-
}
|
|
1004
|
-
return { functionId, mode };
|
|
1188
|
+
return { functionId: String(match.functionId) };
|
|
1005
1189
|
}
|
|
1006
1190
|
/** The identity a call runs under, and the bearer it presents. */
|
|
1007
1191
|
async function planIdentity(client, appId, identity) {
|
|
@@ -1058,15 +1242,9 @@ Reclaiming the key means hard-deleting the row:
|
|
|
1058
1242
|
const client = new ApiClient();
|
|
1059
1243
|
try {
|
|
1060
1244
|
const target = await resolveFunctionByKey(client, resolvedAppId, functionKey);
|
|
1061
|
-
const precheck = precheckMode("invoke", target.mode, functionKey);
|
|
1062
|
-
if (precheck.ok === false) {
|
|
1063
|
-
error(precheck.error);
|
|
1064
|
-
process.exit(1);
|
|
1065
|
-
}
|
|
1066
1245
|
const identity = await planIdentity(client, resolvedAppId, flags.identity);
|
|
1067
1246
|
const body = {
|
|
1068
1247
|
rootInput: flags.input,
|
|
1069
|
-
mode: modeFor("invoke"),
|
|
1070
1248
|
...(options.contextDocId ? { contextDocId: options.contextDocId } : {}),
|
|
1071
1249
|
...(flags.timeoutSeconds
|
|
1072
1250
|
? { timeoutMs: Math.round(flags.timeoutSeconds * 1000) }
|
|
@@ -1075,6 +1253,11 @@ Reclaiming the key means hard-deleting the row:
|
|
|
1075
1253
|
const answer = await runAs(client, resolvedAppId, flags.identity, "invoke", functionKey, (bearer) => client.invokeFunction(resolvedAppId, functionKey, body, {
|
|
1076
1254
|
...(bearer ? { bearer } : {}),
|
|
1077
1255
|
asSystem: identity.asSystem,
|
|
1256
|
+
// #3482 — the ROUTE says which runtime this call means. The
|
|
1257
|
+
// admin-only system door is one route for both, so the body still
|
|
1258
|
+
// names it there and only there.
|
|
1259
|
+
runtime: "request",
|
|
1260
|
+
...(identity.asSystem ? {} : {}),
|
|
1078
1261
|
}));
|
|
1079
1262
|
const envelope = answer.body ?? {};
|
|
1080
1263
|
if (options.json) {
|
|
@@ -1152,11 +1335,6 @@ Reclaiming the key means hard-deleting the row:
|
|
|
1152
1335
|
const client = new ApiClient();
|
|
1153
1336
|
try {
|
|
1154
1337
|
const target = await resolveFunctionByKey(client, resolvedAppId, functionKey);
|
|
1155
|
-
const precheck = precheckMode("start", target.mode, functionKey);
|
|
1156
|
-
if (precheck.ok === false) {
|
|
1157
|
-
error(precheck.error);
|
|
1158
|
-
process.exit(1);
|
|
1159
|
-
}
|
|
1160
1338
|
const identity = await planIdentity(client, resolvedAppId, flags.identity);
|
|
1161
1339
|
// D3448-014 — the task path defaults the context to the acting user's
|
|
1162
1340
|
// `AppUser.rootDocId`, and the app user provisioned for an admin has
|
|
@@ -1173,13 +1351,17 @@ Reclaiming the key means hard-deleting the row:
|
|
|
1173
1351
|
}
|
|
1174
1352
|
const body = {
|
|
1175
1353
|
rootInput: flags.input,
|
|
1176
|
-
|
|
1354
|
+
// #3482 — `--as system` is the ONE door that is a single route for
|
|
1355
|
+
// both runtimes, so it keeps the deprecated body selector; every
|
|
1356
|
+
// other call says it with the route.
|
|
1357
|
+
...(identity.asSystem ? { mode: "task" } : {}),
|
|
1177
1358
|
...(contextDocId ? { contextDocId } : {}),
|
|
1178
1359
|
...(options.runKey ? { runKey: options.runKey } : {}),
|
|
1179
1360
|
};
|
|
1180
1361
|
const answer = await runAs(client, resolvedAppId, flags.identity, "start", functionKey, (bearer) => client.invokeFunction(resolvedAppId, functionKey, body, {
|
|
1181
1362
|
...(bearer ? { bearer } : {}),
|
|
1182
1363
|
asSystem: identity.asSystem,
|
|
1364
|
+
runtime: "task",
|
|
1183
1365
|
}));
|
|
1184
1366
|
const envelope = answer.body ?? {};
|
|
1185
1367
|
if (answer.httpStatus >= 400) {
|