primitive-admin 1.0.59 → 1.0.61
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/bin/primitive.js +6 -0
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.js +22 -8
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +60 -0
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +37 -2
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/connections.d.ts +2 -0
- package/dist/src/commands/connections.js +95 -0
- package/dist/src/commands/connections.js.map +1 -0
- package/dist/src/commands/guides.d.ts +4 -29
- package/dist/src/commands/guides.js +9 -59
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/init.js +12 -4
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/locks.d.ts +8 -0
- package/dist/src/commands/locks.js +160 -0
- package/dist/src/commands/locks.js.map +1 -0
- package/dist/src/commands/sessions.d.ts +2 -0
- package/dist/src/commands/sessions.js +63 -0
- package/dist/src/commands/sessions.js.map +1 -0
- package/dist/src/commands/skill.js +2 -2
- package/dist/src/commands/skill.js.map +1 -1
- package/dist/src/commands/sync.js +120 -13
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/workflows.js +56 -13
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +95 -0
- package/dist/src/lib/api-client.js +137 -15
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/channel.d.ts +30 -0
- package/dist/src/lib/channel.js +68 -0
- package/dist/src/lib/channel.js.map +1 -0
- package/dist/src/lib/generated-allowlist.js +11 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/skill-installer.d.ts +4 -2
- package/dist/src/lib/skill-installer.js +139 -11
- package/dist/src/lib/skill-installer.js.map +1 -1
- package/dist/src/lib/template.d.ts +16 -1
- package/dist/src/lib/template.js +37 -6
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/workflow-fragments.d.ts +23 -0
- package/dist/src/lib/workflow-fragments.js +229 -8
- package/dist/src/lib/workflow-fragments.js.map +1 -1
- package/dist/src/lib/workflow-payload.d.ts +1 -1
- package/dist/src/lib/workflow-payload.js +17 -0
- package/dist/src/lib/workflow-payload.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +57 -1
- package/dist/src/lib/workflow-toml-validator.js +104 -1
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ApiClient } from "../lib/api-client.js";
|
|
2
|
+
import { resolveAppId } from "../lib/config.js";
|
|
3
|
+
import { error, info, formatTable, formatDate, json, } from "../lib/output.js";
|
|
4
|
+
export function registerSessionsCommands(program) {
|
|
5
|
+
const sessions = program
|
|
6
|
+
.command("sessions")
|
|
7
|
+
.description("Inspect a user's authentication sessions")
|
|
8
|
+
.addHelpText("after", `
|
|
9
|
+
A session is one sign-in for a user. The ACTIVE column shows whether it has
|
|
10
|
+
expired — an expired session that has not yet been swept still appears, with
|
|
11
|
+
ACTIVE = false. Authentication tokens are never returned.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
$ primitive sessions list --user 01HXY...
|
|
15
|
+
$ primitive sessions list --user 01HXY... --limit 50
|
|
16
|
+
$ primitive sessions list --user 01HXY... --json
|
|
17
|
+
`);
|
|
18
|
+
sessions
|
|
19
|
+
.command("list")
|
|
20
|
+
.description("List a user's sessions")
|
|
21
|
+
.option("--app <app-id>", "App ID (uses current app if not specified)")
|
|
22
|
+
.requiredOption("--user <user-id>", "List sessions for this user")
|
|
23
|
+
.option("--limit <n>", "Maximum rows to return (default 25, max 100)")
|
|
24
|
+
.option("--cursor <cursor>", "Pagination cursor from a previous page")
|
|
25
|
+
.option("--json", "Output as JSON")
|
|
26
|
+
.action(async (options) => {
|
|
27
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
28
|
+
const client = new ApiClient();
|
|
29
|
+
const limit = options.limit ? Number(options.limit) : undefined;
|
|
30
|
+
if (options.limit !== undefined &&
|
|
31
|
+
(!Number.isInteger(limit) || limit <= 0)) {
|
|
32
|
+
error("--limit must be a positive integer.");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const { items, nextCursor } = await client.listSessions(resolvedAppId, options.user, { limit, cursor: options.cursor });
|
|
37
|
+
if (options.json) {
|
|
38
|
+
json({ items, nextCursor });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!items || items.length === 0) {
|
|
42
|
+
info("No sessions found.");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log(formatTable(items, [
|
|
46
|
+
{ header: "SESSION", key: "sessionId" },
|
|
47
|
+
{ header: "USER", key: "userId" },
|
|
48
|
+
{ header: "ACTIVE", key: "active" },
|
|
49
|
+
{ header: "CREATED", key: "createdAt", format: formatDate },
|
|
50
|
+
{ header: "LAST ACTIVITY", key: "lastActivity", format: formatDate },
|
|
51
|
+
{ header: "EXPIRES", key: "expiresAt", format: formatDate },
|
|
52
|
+
]));
|
|
53
|
+
if (nextCursor) {
|
|
54
|
+
info(`More results available. Re-run with --cursor ${nextCursor}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
error(err.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/commands/sessions.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,8 +6,8 @@ export function registerSkillCommands(program) {
|
|
|
6
6
|
skill
|
|
7
7
|
.command("install")
|
|
8
8
|
.description("Install or update the Primitive platform skill for Claude Code")
|
|
9
|
-
.action(() => {
|
|
10
|
-
if (!installSkillExplicit()) {
|
|
9
|
+
.action(async () => {
|
|
10
|
+
if (!(await installSkillExplicit())) {
|
|
11
11
|
process.exitCode = 1;
|
|
12
12
|
}
|
|
13
13
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/commands/skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE9F,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,qDAAqD,CAAC,CAAC;IAEtE,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,gEAAgE,CAAC;SAC7E,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/commands/skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE9F,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,qDAAqD,CAAC,CAAC;IAEtE,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,gEAAgE,CAAC;SAC7E,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC,CAAC,MAAM,oBAAoB,EAAE,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YACtB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,wCAAwC,CAAC;SACrD,MAAM,CAAC,GAAG,EAAE;QACX,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -105,6 +105,15 @@ function canonicalJsonStringify(value) {
|
|
|
105
105
|
if (Array.isArray(value)) {
|
|
106
106
|
return "[" + value.map((v) => canonicalJsonStringify(v)).join(",") + "]";
|
|
107
107
|
}
|
|
108
|
+
// Honor `toJSON` before the key walk. A `Date` — or smol-toml's `TomlDate`,
|
|
109
|
+
// what a TOML datetime literal parses to — has no enumerable own keys, so
|
|
110
|
+
// the `Object.keys()` walk below would collapse it to "{}" and every
|
|
111
|
+
// datetime would hash identically (issue #1919). Both implement `toJSON`,
|
|
112
|
+
// returning the ISO string; recursing on it makes date-like values (nested
|
|
113
|
+
// ones too) participate in the hash. Matches JSON.stringify's own semantics.
|
|
114
|
+
if (typeof value.toJSON === "function") {
|
|
115
|
+
return canonicalJsonStringify(value.toJSON());
|
|
116
|
+
}
|
|
108
117
|
const keys = Object.keys(value).sort();
|
|
109
118
|
return ("{" +
|
|
110
119
|
keys
|
|
@@ -913,6 +922,16 @@ export function serializeWorkflow(workflow, draft, configs, logger) {
|
|
|
913
922
|
// as a data-loss fallback (issue #1446).
|
|
914
923
|
inputSchema: serializeWorkflowSchemaField(workflow.inputSchema, "inputSchema", workflow.workflowKey, log),
|
|
915
924
|
outputSchema: serializeWorkflowSchemaField(workflow.outputSchema, "outputSchema", workflow.workflowKey, log),
|
|
925
|
+
// #1972 — declarative run-scoped lock (`[workflow.lock]`, #1518 Phase 3).
|
|
926
|
+
// The GET response returns `lock` parsed (an object, null when unset —
|
|
927
|
+
// like `metadataManifest`). Emit the nested `[workflow.lock]` table only
|
|
928
|
+
// when present so a fresh pull → push round-trips it and an unset lock
|
|
929
|
+
// doesn't write a noisy empty table (nor a false `sync diff`). Before this
|
|
930
|
+
// pull dropped the lock entirely, so a pull → push cycle silently cleared
|
|
931
|
+
// a server-side lock.
|
|
932
|
+
lock: workflow.lock && typeof workflow.lock === "object"
|
|
933
|
+
? workflow.lock
|
|
934
|
+
: undefined,
|
|
916
935
|
},
|
|
917
936
|
// Use active config steps, fall back to draft
|
|
918
937
|
steps,
|
|
@@ -3024,7 +3043,8 @@ Directory Structure:
|
|
|
3024
3043
|
// (no more `process.exit` bypass class). The per-loop validators below
|
|
3025
3044
|
// remain as defense-in-depth but are converted to throws too.
|
|
3026
3045
|
const preflightValidationErrors = [];
|
|
3027
|
-
// Validate all workflow TOMLs (
|
|
3046
|
+
// Validate all workflow TOMLs (#685 misnested-header check and the
|
|
3047
|
+
// #1447 system + accessRule lint).
|
|
3028
3048
|
const preflightWorkflowsDir = join(configDir, "workflows");
|
|
3029
3049
|
if (existsSync(preflightWorkflowsDir)) {
|
|
3030
3050
|
for (const file of readdirSync(preflightWorkflowsDir).filter((f) => f.endsWith(".toml"))) {
|
|
@@ -4445,10 +4465,12 @@ Directory Structure:
|
|
|
4445
4465
|
info(` Skipped workflows/${file} — platform-owned workflow "${platformOwnedKey}" (managed by the platform)`);
|
|
4446
4466
|
continue;
|
|
4447
4467
|
}
|
|
4448
|
-
//
|
|
4449
|
-
//
|
|
4450
|
-
//
|
|
4451
|
-
// place to catch the footgun
|
|
4468
|
+
// Reject misnested headers (#685, e.g. [steps.<id>.request]) and
|
|
4469
|
+
// the #1447 runAs="system" + accessRule dead-config combo before
|
|
4470
|
+
// pushing. The runtime silently ignores fields outside the
|
|
4471
|
+
// allowlist, so this is the only place to catch the footgun, and
|
|
4472
|
+
// the server rejects a system accessRule (#1258) — flagging it
|
|
4473
|
+
// here keeps the push all-or-nothing. Validation happens BEFORE the
|
|
4452
4474
|
// skip-if-unchanged check so a previously-pushed-but-broken
|
|
4453
4475
|
// file gets a clear diagnostic on every push attempt.
|
|
4454
4476
|
// Issue #976: validation is now front-loaded before any apply
|
|
@@ -4936,6 +4958,28 @@ Directory Structure:
|
|
|
4936
4958
|
const validateExpectedModifiedAt = options.force
|
|
4937
4959
|
? undefined
|
|
4938
4960
|
: existingEntry?.modifiedAt;
|
|
4961
|
+
// Issue #1336 (codex round-1 P1): the final post-push rule content
|
|
4962
|
+
// of every op this push lands. When the push ALSO changes the
|
|
4963
|
+
// manifest, the server re-lints child ops against the new manifest;
|
|
4964
|
+
// forwarding each op's proposed `access`/`params` lets that re-lint
|
|
4965
|
+
// evaluate the op's post-push form instead of the stale stored row,
|
|
4966
|
+
// so an atomic manifest-rename + op-rewrite push is not falsely
|
|
4967
|
+
// rejected. A field is sent as value-or-null (config-as-code: the
|
|
4968
|
+
// TOML op IS the final state).
|
|
4969
|
+
const pendingOpUpdates = operations.map((op) => ({
|
|
4970
|
+
name: op.name,
|
|
4971
|
+
access: typeof op.access === "string" ? op.access : null,
|
|
4972
|
+
params: op.params ?? null,
|
|
4973
|
+
}));
|
|
4974
|
+
// Issue #1336 (codex round-1 P1/P2): the declared-access manifest
|
|
4975
|
+
// this push proposes (the parsed object, or null when the type
|
|
4976
|
+
// declares none / is clearing it). Forwarded as
|
|
4977
|
+
// `metadataManifestOverride` on the op dry-runs so op-access lints
|
|
4978
|
+
// run against the manifest the same push is landing, not the stale
|
|
4979
|
+
// stored one (existing type: a newly-declared key is honored; fresh
|
|
4980
|
+
// type: an undeclared ref is caught instead of skipped).
|
|
4981
|
+
const proposedManifest = typeConfig.metadataManifest ??
|
|
4982
|
+
null;
|
|
4939
4983
|
try {
|
|
4940
4984
|
// 1. Schema-edit gate (type-config PATCH dry-run) — only when the
|
|
4941
4985
|
// type already exists on the server and we'd actually PATCH a
|
|
@@ -4948,13 +4992,53 @@ Directory Structure:
|
|
|
4948
4992
|
// type-config dry-run at all.
|
|
4949
4993
|
if (existingEntry && typeLevelChange) {
|
|
4950
4994
|
const validateUpdateData = computeTypeUpdateData();
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4995
|
+
const baseValidate = pendingOpDeletes.length > 0
|
|
4996
|
+
? { ...validateUpdateData, pendingOpDeletes, finalOpNames }
|
|
4997
|
+
: validateUpdateData;
|
|
4998
|
+
await client.updateDatabaseTypeConfig(resolvedAppId, dbType,
|
|
4999
|
+
// Issue #1336 (P1): forward the pending op rewrites so the
|
|
5000
|
+
// server's child-op re-lint uses each op's post-push content.
|
|
5001
|
+
{ ...baseValidate, pendingOpUpdates }, validateExpectedModifiedAt, {
|
|
5002
|
+
dryRun: true,
|
|
5003
|
+
acceptWarnings: !!options.acceptWarnings,
|
|
5004
|
+
});
|
|
5005
|
+
}
|
|
5006
|
+
else if (!existingEntry) {
|
|
5007
|
+
// Fresh type: run the config CREATE dry-run (issue #1336, codex
|
|
5008
|
+
// round-1 P2). Before this, a brand-new type's inline config
|
|
5009
|
+
// rules (`defaultAccess`, `metadataAccess`, autoPopulatedFields,
|
|
5010
|
+
// triggers) and its manifest were never dry-run — a malformed
|
|
5011
|
+
// rule reported a clean `sync push --dry-run` then failed only on
|
|
5012
|
+
// the real POST. Mirror the apply-path create payload below.
|
|
5013
|
+
const createDryRunData = { databaseType: dbType };
|
|
5014
|
+
if (typeConfig.ruleSetId)
|
|
5015
|
+
createDryRunData.ruleSetId = typeConfig.ruleSetId;
|
|
5016
|
+
if (typeConfig.triggers)
|
|
5017
|
+
createDryRunData.triggers = typeConfig.triggers;
|
|
5018
|
+
if (typeConfig.metadataAccess)
|
|
5019
|
+
createDryRunData.metadataAccess = typeConfig.metadataAccess;
|
|
5020
|
+
if (typeConfig.defaultAccess)
|
|
5021
|
+
createDryRunData.defaultAccess = typeConfig.defaultAccess;
|
|
5022
|
+
if (typeConfig.autoPopulatedFields)
|
|
5023
|
+
createDryRunData.autoPopulatedFields =
|
|
5024
|
+
typeConfig.autoPopulatedFields;
|
|
5025
|
+
if (typeConfig.timestamps)
|
|
5026
|
+
createDryRunData.timestamps = typeConfig.timestamps;
|
|
5027
|
+
if (typeConfig.schema)
|
|
5028
|
+
createDryRunData.schema = typeConfig.schema;
|
|
5029
|
+
if (typeConfig.metadataManifest)
|
|
5030
|
+
createDryRunData.metadataManifest = typeConfig.metadataManifest;
|
|
5031
|
+
try {
|
|
5032
|
+
await client.createDatabaseTypeConfig(resolvedAppId, createDryRunData, { dryRun: true });
|
|
5033
|
+
}
|
|
5034
|
+
catch (createErr) {
|
|
5035
|
+
// A 409 means the type already exists server-side (local sync
|
|
5036
|
+
// state was lost); the fresh-type inline-rule validation does
|
|
5037
|
+
// not apply and the real push reconciles separately. Any other
|
|
5038
|
+
// error is a genuine create-time validation failure — re-throw
|
|
5039
|
+
// so the catch below reports it.
|
|
5040
|
+
if (createErr?.statusCode !== 409)
|
|
5041
|
+
throw createErr;
|
|
4958
5042
|
}
|
|
4959
5043
|
}
|
|
4960
5044
|
// 2. Op-edit gate (op create/update dry-run) for every op in the
|
|
@@ -5012,7 +5096,14 @@ Directory Structure:
|
|
|
5012
5096
|
access: op.access,
|
|
5013
5097
|
definition: op.definition,
|
|
5014
5098
|
params: op.params,
|
|
5015
|
-
}, options.force ? undefined : existingOp.modifiedAt, {
|
|
5099
|
+
}, options.force ? undefined : existingOp.modifiedAt, {
|
|
5100
|
+
dryRun: true,
|
|
5101
|
+
schemaOverride: proposedSchema,
|
|
5102
|
+
// Issue #1336 (P1/P2): gate the op-access lint against
|
|
5103
|
+
// the manifest THIS push is landing, not the stale
|
|
5104
|
+
// stored one.
|
|
5105
|
+
metadataManifestOverride: proposedManifest,
|
|
5106
|
+
});
|
|
5016
5107
|
}
|
|
5017
5108
|
else {
|
|
5018
5109
|
await client.createDatabaseTypeOperation(resolvedAppId, dbType, {
|
|
@@ -5025,6 +5116,10 @@ Directory Structure:
|
|
|
5025
5116
|
}, {
|
|
5026
5117
|
dryRun: true,
|
|
5027
5118
|
schemaOverride: proposedSchema,
|
|
5119
|
+
// Issue #1336 (P1/P2): as above — a fresh-type op dry-run
|
|
5120
|
+
// now lints against the proposed manifest instead of
|
|
5121
|
+
// skipping the lint entirely.
|
|
5122
|
+
metadataManifestOverride: proposedManifest,
|
|
5028
5123
|
// Issue #915 (defect a, follow-up): for a fresh type the
|
|
5029
5124
|
// server has no stored config to read `defaultAccess`
|
|
5030
5125
|
// from, so an op that omits `access` to inherit the
|
|
@@ -5209,6 +5304,18 @@ Directory Structure:
|
|
|
5209
5304
|
// exclusion (see derivation above).
|
|
5210
5305
|
updateData.finalOpNames = finalOpNames;
|
|
5211
5306
|
}
|
|
5307
|
+
// Issue #1336 (codex round-2 P1): forward the pending op
|
|
5308
|
+
// rewrites on the REAL mutating PATCH too, mirroring the
|
|
5309
|
+
// dry-run validate pass above. Without this, when the same
|
|
5310
|
+
// push replaces a manifest key AND rewrites an op that
|
|
5311
|
+
// references the old key, the server's child-op re-lint runs
|
|
5312
|
+
// against the stale stored op → INVALID_RULE → the config
|
|
5313
|
+
// PATCH aborts before the op PATCH lands. Sending the final
|
|
5314
|
+
// op forms lets the re-lint evaluate each op's post-push
|
|
5315
|
+
// content, so the atomic manifest-rename + op-rewrite push
|
|
5316
|
+
// converges. Sent unconditionally (as the dry-run does); a
|
|
5317
|
+
// field absent from a claim means "not changed" server-side.
|
|
5318
|
+
updateData.pendingOpUpdates = pendingOpUpdates;
|
|
5212
5319
|
if (typeLevelChange) {
|
|
5213
5320
|
changes.push({ type: "database-type", action: "update", key: dbType });
|
|
5214
5321
|
const updated = await client.updateDatabaseTypeConfig(resolvedAppId, dbType, updateData, expectedModifiedAt, {
|