primitive-admin 1.0.61 → 1.0.63
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 +125 -69
- package/assets/skill/skills/primitive-platform/SKILL.md +336 -37
- package/dist/bin/primitive.js +25 -14
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/analytics.js +36 -16
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.js +52 -188
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +13 -153
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/collection-type-configs.js +14 -100
- package/dist/src/commands/collection-type-configs.js.map +1 -1
- package/dist/src/commands/collections.js +47 -8
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/config.d.ts +46 -0
- package/dist/src/commands/config.js +479 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/connections.js +12 -7
- package/dist/src/commands/connections.js.map +1 -1
- package/dist/src/commands/cron-triggers.js +84 -176
- package/dist/src/commands/cron-triggers.js.map +1 -1
- package/dist/src/commands/database-type-configs.d.ts +2 -0
- package/dist/src/commands/database-type-configs.js +171 -0
- package/dist/src/commands/database-type-configs.js.map +1 -0
- package/dist/src/commands/database-types.js +4 -4
- package/dist/src/commands/database-types.js.map +1 -1
- package/dist/src/commands/databases.d.ts +63 -0
- package/dist/src/commands/databases.js +649 -87
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.js +869 -12
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.js +21 -128
- package/dist/src/commands/email-templates.js.map +1 -1
- package/dist/src/commands/env.d.ts +12 -1
- package/dist/src/commands/env.js +115 -26
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/feature-flags.d.ts +14 -0
- package/dist/src/commands/feature-flags.js +116 -0
- package/dist/src/commands/feature-flags.js.map +1 -0
- package/dist/src/commands/group-type-configs.js +14 -109
- package/dist/src/commands/group-type-configs.js.map +1 -1
- package/dist/src/commands/init.d.ts +24 -1
- package/dist/src/commands/init.js +1194 -279
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +166 -435
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/metadata-category-configs.d.ts +12 -0
- package/dist/src/commands/metadata-category-configs.js +112 -0
- package/dist/src/commands/metadata-category-configs.js.map +1 -0
- package/dist/src/commands/metadata.js +33 -141
- package/dist/src/commands/metadata.js.map +1 -1
- package/dist/src/commands/prompts.js +206 -563
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +1 -1
- package/dist/src/commands/rule-sets.js +16 -132
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/scripts.d.ts +10 -0
- package/dist/src/commands/scripts.js +203 -241
- package/dist/src/commands/scripts.js.map +1 -1
- package/dist/src/commands/sessions.js +17 -5
- package/dist/src/commands/sessions.js.map +1 -1
- package/dist/src/commands/sync-app-settings.d.ts +113 -70
- package/dist/src/commands/sync-app-settings.js +193 -242
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +1904 -51
- package/dist/src/commands/sync.js +9468 -1790
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.js +8 -2
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.js +92 -1
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/vars.js +44 -58
- package/dist/src/commands/vars.js.map +1 -1
- package/dist/src/commands/webhooks.js +318 -156
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.d.ts +49 -12
- package/dist/src/commands/workflows.js +652 -1086
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/access-rule-display.d.ts +21 -0
- package/dist/src/lib/access-rule-display.js +34 -0
- package/dist/src/lib/access-rule-display.js.map +1 -0
- package/dist/src/lib/api-client.d.ts +541 -61
- package/dist/src/lib/api-client.js +559 -102
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +180 -25
- package/dist/src/lib/app-settings-descriptor.js +403 -82
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/canonical-json.d.ts +12 -0
- package/dist/src/lib/canonical-json.js +35 -0
- package/dist/src/lib/canonical-json.js.map +1 -0
- package/dist/src/lib/cli-manifest.d.ts +8 -0
- package/dist/src/lib/cli-manifest.js +1 -0
- package/dist/src/lib/cli-manifest.js.map +1 -1
- package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -2
- package/dist/src/lib/codegen-shared/generatedFiles.js +9 -2
- package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +6 -6
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +8 -8
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
- package/dist/src/lib/config-object-descriptor.d.ts +127 -0
- package/dist/src/lib/config-object-descriptor.js +658 -0
- package/dist/src/lib/config-object-descriptor.js.map +1 -0
- package/dist/src/lib/config-payload.d.ts +85 -0
- package/dist/src/lib/config-payload.js +116 -0
- package/dist/src/lib/config-payload.js.map +1 -0
- package/dist/src/lib/config-surface.d.ts +130 -0
- package/dist/src/lib/config-surface.js +300 -0
- package/dist/src/lib/config-surface.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +10 -0
- package/dist/src/lib/credentials-store.js +23 -0
- package/dist/src/lib/credentials-store.js.map +1 -1
- package/dist/src/lib/data-input.d.ts +23 -0
- package/dist/src/lib/data-input.js +50 -0
- package/dist/src/lib/data-input.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTemplates.js +1 -1
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +1 -1
- package/dist/src/lib/env-resolver-core.d.ts +187 -0
- package/dist/src/lib/env-resolver-core.js +325 -0
- package/dist/src/lib/env-resolver-core.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +28 -6
- package/dist/src/lib/env-resolver.js +44 -32
- package/dist/src/lib/env-resolver.js.map +1 -1
- package/dist/src/lib/generated-allowlist.js +12 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +682 -0
- package/dist/src/lib/generated-config-surfaces.js +4058 -0
- package/dist/src/lib/generated-config-surfaces.js.map +1 -0
- package/dist/src/lib/generated-template-lint.d.ts +212 -0
- package/dist/src/lib/generated-template-lint.js +624 -0
- package/dist/src/lib/generated-template-lint.js.map +1 -0
- package/dist/src/lib/init-adopt.d.ts +16 -0
- package/dist/src/lib/init-adopt.js +34 -0
- package/dist/src/lib/init-adopt.js.map +1 -0
- package/dist/src/lib/init-assets.d.ts +39 -0
- package/dist/src/lib/init-assets.js +97 -0
- package/dist/src/lib/init-assets.js.map +1 -0
- package/dist/src/lib/init-client-platforms.d.ts +14 -0
- package/dist/src/lib/init-client-platforms.js +71 -0
- package/dist/src/lib/init-client-platforms.js.map +1 -0
- package/dist/src/lib/init-config.d.ts +44 -5
- package/dist/src/lib/init-config.js +80 -7
- package/dist/src/lib/init-config.js.map +1 -1
- package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
- package/dist/src/lib/init-email-redirect-uris.js +46 -0
- package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
- package/dist/src/lib/init-ios-links.d.ts +90 -0
- package/dist/src/lib/init-ios-links.js +218 -0
- package/dist/src/lib/init-ios-links.js.map +1 -0
- package/dist/src/lib/init-plan.d.ts +80 -0
- package/dist/src/lib/init-plan.js +95 -0
- package/dist/src/lib/init-plan.js.map +1 -0
- package/dist/src/lib/init-production-env.d.ts +48 -0
- package/dist/src/lib/init-production-env.js +59 -0
- package/dist/src/lib/init-production-env.js.map +1 -0
- package/dist/src/lib/init-schema.d.ts +74 -0
- package/dist/src/lib/init-schema.js +358 -0
- package/dist/src/lib/init-schema.js.map +1 -0
- package/dist/src/lib/init-xcode.d.ts +33 -0
- package/dist/src/lib/init-xcode.js +114 -0
- package/dist/src/lib/init-xcode.js.map +1 -0
- package/dist/src/lib/integration-request-config.d.ts +30 -0
- package/dist/src/lib/integration-request-config.js +145 -0
- package/dist/src/lib/integration-request-config.js.map +1 -0
- package/dist/src/lib/integration-selector.d.ts +42 -0
- package/dist/src/lib/integration-selector.js +46 -0
- package/dist/src/lib/integration-selector.js.map +1 -0
- package/dist/src/lib/local-state.d.ts +55 -0
- package/dist/src/lib/local-state.js +167 -0
- package/dist/src/lib/local-state.js.map +1 -0
- package/dist/src/lib/local-test-cases.d.ts +63 -0
- package/dist/src/lib/local-test-cases.js +135 -0
- package/dist/src/lib/local-test-cases.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +568 -0
- package/dist/src/lib/log-inspection.js +639 -0
- package/dist/src/lib/log-inspection.js.map +1 -0
- package/dist/src/lib/migration-nag.d.ts +2 -2
- package/dist/src/lib/migration-nag.js +3 -3
- package/dist/src/lib/object-status-filter.d.ts +22 -0
- package/dist/src/lib/object-status-filter.js +45 -0
- package/dist/src/lib/object-status-filter.js.map +1 -0
- package/dist/src/lib/output.d.ts +24 -1
- package/dist/src/lib/output.js +42 -1
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/package-manager.d.ts +140 -0
- package/dist/src/lib/package-manager.js +305 -0
- package/dist/src/lib/package-manager.js.map +1 -0
- package/dist/src/lib/paginate.d.ts +50 -0
- package/dist/src/lib/paginate.js +53 -0
- package/dist/src/lib/paginate.js.map +1 -1
- package/dist/src/lib/platform-owned.d.ts +29 -3
- package/dist/src/lib/platform-owned.js +42 -3
- package/dist/src/lib/platform-owned.js.map +1 -1
- package/dist/src/lib/project-config.d.ts +8 -0
- package/dist/src/lib/project-config.js +30 -5
- package/dist/src/lib/project-config.js.map +1 -1
- package/dist/src/lib/query-operators.d.ts +1 -1
- package/dist/src/lib/query-operators.js +1 -1
- package/dist/src/lib/record-filter.d.ts +18 -0
- package/dist/src/lib/record-filter.js +55 -0
- package/dist/src/lib/record-filter.js.map +1 -0
- package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
- package/dist/src/lib/resolve-init-dev-port.js +55 -0
- package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
- package/dist/src/lib/resolve-init-server.d.ts +64 -0
- package/dist/src/lib/resolve-init-server.js +77 -0
- package/dist/src/lib/resolve-init-server.js.map +1 -0
- package/dist/src/lib/resolve-platform.d.ts +43 -14
- package/dist/src/lib/resolve-platform.js +74 -12
- package/dist/src/lib/resolve-platform.js.map +1 -1
- package/dist/src/lib/run-status.d.ts +19 -0
- package/dist/src/lib/run-status.generated.d.ts +39 -0
- package/dist/src/lib/run-status.generated.js +66 -0
- package/dist/src/lib/run-status.generated.js.map +1 -0
- package/dist/src/lib/run-status.js +19 -0
- package/dist/src/lib/run-status.js.map +1 -0
- package/dist/src/lib/server-text-normalization.d.ts +51 -0
- package/dist/src/lib/server-text-normalization.js +90 -0
- package/dist/src/lib/server-text-normalization.js.map +1 -0
- package/dist/src/lib/server-url.d.ts +22 -0
- package/dist/src/lib/server-url.js +33 -0
- package/dist/src/lib/server-url.js.map +1 -0
- package/dist/src/lib/signing-secret-status.d.ts +81 -0
- package/dist/src/lib/signing-secret-status.js +116 -0
- package/dist/src/lib/signing-secret-status.js.map +1 -0
- package/dist/src/lib/skill-installer.js +6 -3
- package/dist/src/lib/skill-installer.js.map +1 -1
- package/dist/src/lib/snapshots.d.ts +2 -2
- package/dist/src/lib/snapshots.js +2 -2
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +15 -2
- package/dist/src/lib/swift-codegen/dbGenerator.js +250 -18
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.d.ts +11 -2
- package/dist/src/lib/swift-codegen/generator.js +181 -7
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +7 -3
- package/dist/src/lib/swift-codegen/siblingSymbols.js +4 -1
- package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -1
- package/dist/src/lib/sync-dir-selector.d.ts +21 -0
- package/dist/src/lib/sync-dir-selector.js +30 -0
- package/dist/src/lib/sync-dir-selector.js.map +1 -0
- package/dist/src/lib/sync-paths.d.ts +39 -0
- package/dist/src/lib/sync-paths.js +70 -2
- package/dist/src/lib/sync-paths.js.map +1 -1
- package/dist/src/lib/sync-resource-types.d.ts +138 -31
- package/dist/src/lib/sync-resource-types.js +241 -47
- package/dist/src/lib/sync-resource-types.js.map +1 -1
- package/dist/src/lib/sync-selectors.d.ts +95 -0
- package/dist/src/lib/sync-selectors.js +228 -0
- package/dist/src/lib/sync-selectors.js.map +1 -0
- package/dist/src/lib/template.d.ts +62 -4
- package/dist/src/lib/template.js +147 -39
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/test-case-file-names.d.ts +40 -0
- package/dist/src/lib/test-case-file-names.js +91 -0
- package/dist/src/lib/test-case-file-names.js.map +1 -0
- package/dist/src/lib/test-case-keys.d.ts +29 -0
- package/dist/src/lib/test-case-keys.js +55 -0
- package/dist/src/lib/test-case-keys.js.map +1 -0
- package/dist/src/lib/test-case-variables.d.ts +15 -0
- package/dist/src/lib/test-case-variables.js +29 -0
- package/dist/src/lib/test-case-variables.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +4 -4
- package/dist/src/lib/toml-database-config.js +27 -34
- package/dist/src/lib/toml-database-config.js.map +1 -1
- package/dist/src/lib/toml-metadata-config.d.ts +9 -5
- package/dist/src/lib/toml-metadata-config.js +40 -45
- package/dist/src/lib/toml-metadata-config.js.map +1 -1
- package/dist/src/lib/toml-native-form.d.ts +1 -1
- package/dist/src/lib/toml-native-form.js +1 -1
- package/dist/src/lib/toml-params-validator.d.ts +1 -1
- package/dist/src/lib/toml-params-validator.js +1 -1
- package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
- package/dist/src/lib/toml-scalar-edit.js +283 -0
- package/dist/src/lib/toml-scalar-edit.js.map +1 -0
- package/dist/src/lib/user-selector.d.ts +24 -0
- package/dist/src/lib/user-selector.js +33 -0
- package/dist/src/lib/user-selector.js.map +1 -0
- package/dist/src/lib/version-check.d.ts +25 -0
- package/dist/src/lib/version-check.js +77 -8
- package/dist/src/lib/version-check.js.map +1 -1
- package/dist/src/lib/watch.d.ts +121 -0
- package/dist/src/lib/watch.js +169 -0
- package/dist/src/lib/watch.js.map +1 -0
- package/dist/src/lib/web-url.d.ts +40 -0
- package/dist/src/lib/web-url.js +76 -0
- package/dist/src/lib/web-url.js.map +1 -0
- package/dist/src/lib/workflow-apply.d.ts +69 -45
- package/dist/src/lib/workflow-apply.js +99 -72
- package/dist/src/lib/workflow-apply.js.map +1 -1
- package/dist/src/lib/workflow-codegen/generator.js +4 -4
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +11 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js +5 -0
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
- package/dist/src/lib/workflow-config-apply.d.ts +70 -0
- package/dist/src/lib/workflow-config-apply.js +137 -0
- package/dist/src/lib/workflow-config-apply.js.map +1 -0
- package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
- package/dist/src/lib/workflow-config-sidecar.js +96 -0
- package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
- package/dist/src/lib/workflow-defaults.d.ts +29 -0
- package/dist/src/lib/workflow-defaults.js +41 -0
- package/dist/src/lib/workflow-defaults.js.map +1 -0
- package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
- package/dist/src/lib/workflow-include-preserve.js +286 -0
- package/dist/src/lib/workflow-include-preserve.js.map +1 -0
- package/dist/src/lib/workflow-payload.d.ts +31 -20
- package/dist/src/lib/workflow-payload.js +100 -110
- package/dist/src/lib/workflow-payload.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +37 -16
- package/dist/src/lib/workflow-toml-validator.js +141 -16
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/dist/src/types/index.d.ts +78 -22
- package/dist/src/validators.d.ts +7 -7
- package/dist/src/validators.js +6 -6
- package/package.json +11 -6
- package/dist/src/commands/settings.d.ts +0 -15
- package/dist/src/commands/settings.js +0 -102
- package/dist/src/commands/settings.js.map +0 -1
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set one scalar key in a TOML file, textually (issue #2645).
|
|
3
|
+
*
|
|
4
|
+
* `config set` exists so a script can change a single configuration field without
|
|
5
|
+
* a here-document. It must not parse the file and re-stringify it: the CLI's
|
|
6
|
+
* TOML layer is a `smol-toml` wrapper (`config-toml.ts`) with no comment or
|
|
7
|
+
* ordering fidelity, so a round-trip edit would quietly strip an author's
|
|
8
|
+
* comments and reorder their keys. The precedents for a textual splice are
|
|
9
|
+
* `databases schema generate` (`commands/databases.ts`) and
|
|
10
|
+
* `workflow-include-preserve.ts`.
|
|
11
|
+
*
|
|
12
|
+
* The edit is deliberately narrow — one scalar, in one table:
|
|
13
|
+
* - the value is replaced in place when the key exists,
|
|
14
|
+
* - the key is appended to the end of its table when it does not,
|
|
15
|
+
* - the table is appended to the file when IT does not,
|
|
16
|
+
* - anything else (a table path, an array value, a path that descends
|
|
17
|
+
* through an array of tables) is refused rather than guessed at.
|
|
18
|
+
*
|
|
19
|
+
* Every result is re-parsed before it is returned, so a splice that would
|
|
20
|
+
* produce invalid TOML fails instead of writing a broken file.
|
|
21
|
+
*/
|
|
22
|
+
import { parseConfigToml, stringifyConfigToml } from "./config-toml.js";
|
|
23
|
+
import { canonicalJsonStringify } from "./canonical-json.js";
|
|
24
|
+
/** A `config set` edit that must not be applied. */
|
|
25
|
+
export class TomlEditError extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "TomlEditError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read a command-line value into the scalar it denotes.
|
|
33
|
+
*
|
|
34
|
+
* `true`/`false` are booleans and a numeric literal is a number, so
|
|
35
|
+
* `config set workflow/x workflow.syncCallable=true` writes a boolean rather than
|
|
36
|
+
* the string `"true"`. Wrapping the value in double quotes forces a string, so
|
|
37
|
+
* a field that genuinely holds `"true"` is still expressible.
|
|
38
|
+
*/
|
|
39
|
+
export function parseScalarLiteral(raw) {
|
|
40
|
+
if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
|
|
41
|
+
return raw.slice(1, -1);
|
|
42
|
+
}
|
|
43
|
+
if (raw === "true")
|
|
44
|
+
return true;
|
|
45
|
+
if (raw === "false")
|
|
46
|
+
return false;
|
|
47
|
+
// Only a complete numeric literal counts; `1.2.3` and `12px` stay strings.
|
|
48
|
+
if (/^-?\d+(\.\d+)?$/.test(raw))
|
|
49
|
+
return Number(raw);
|
|
50
|
+
return raw;
|
|
51
|
+
}
|
|
52
|
+
/** TOML bare keys need no quoting; anything else is quoted. */
|
|
53
|
+
function renderKey(key) {
|
|
54
|
+
return /^[A-Za-z0-9_-]+$/.test(key) ? key : JSON.stringify(key);
|
|
55
|
+
}
|
|
56
|
+
/** Render `key = value` the way smol-toml would, so escaping is not hand-rolled. */
|
|
57
|
+
function renderAssignment(key, value) {
|
|
58
|
+
// `stringifyConfigToml({ k: v })` yields `k = <value>`; take the value half so
|
|
59
|
+
// string escaping, float formatting and integer formatting all come from the
|
|
60
|
+
// one TOML serializer rather than a second implementation here.
|
|
61
|
+
const rendered = stringifyConfigToml({ __v: value }).trim();
|
|
62
|
+
const eq = rendered.indexOf("=");
|
|
63
|
+
const valueText = rendered.slice(eq + 1).trim();
|
|
64
|
+
return `${renderKey(key)} = ${valueText}`;
|
|
65
|
+
}
|
|
66
|
+
/** Match a table header line, returning its dotted path. */
|
|
67
|
+
function tableHeaderPath(line) {
|
|
68
|
+
const match = line.match(/^\s*\[([^\[\]]+)\]\s*(#.*)?$/);
|
|
69
|
+
return match ? match[1].trim() : null;
|
|
70
|
+
}
|
|
71
|
+
/** True for an array-of-tables header, e.g. `[[steps]]`. */
|
|
72
|
+
function isArrayTableHeader(line) {
|
|
73
|
+
return /^\s*\[\[[^\[\]]+\]\]\s*(#.*)?$/.test(line);
|
|
74
|
+
}
|
|
75
|
+
/** True when `line` assigns `key` at the start of the line. */
|
|
76
|
+
function assignsKey(line, key) {
|
|
77
|
+
const match = line.match(/^\s*("([^"]*)"|'([^']*)'|[A-Za-z0-9_-]+)\s*=/);
|
|
78
|
+
if (!match)
|
|
79
|
+
return false;
|
|
80
|
+
const found = match[2] ?? match[3] ?? match[1];
|
|
81
|
+
return found === key;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Replace or insert `path` in `text`.
|
|
85
|
+
*
|
|
86
|
+
* `path` is dotted TOML: the last segment is the key, the rest is the table it
|
|
87
|
+
* lives in (`workflow.status` → key `status` in table `[workflow]`). A
|
|
88
|
+
* single-segment path addresses a root-level key.
|
|
89
|
+
*/
|
|
90
|
+
export function setTomlScalar(text, path, value) {
|
|
91
|
+
if (!path || !path.trim()) {
|
|
92
|
+
throw new TomlEditError("a field path is required, e.g. workflow.status");
|
|
93
|
+
}
|
|
94
|
+
const segments = path.split(".").map((s) => s.trim());
|
|
95
|
+
if (segments.some((s) => s === "")) {
|
|
96
|
+
throw new TomlEditError(`'${path}' is not a valid dotted TOML path`);
|
|
97
|
+
}
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = parseConfigToml(text);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
throw new TomlEditError(`the file is not valid TOML: ${e?.message || e}`);
|
|
104
|
+
}
|
|
105
|
+
// Refuse a path the file writes in a form a dotted path cannot address.
|
|
106
|
+
// A quoted key containing a dot (`"a.b" = 1`) parses to the single key `a.b`,
|
|
107
|
+
// which `workflow.a.b` cannot reach — the splice would append a NEW `[a]`
|
|
108
|
+
// table and leave the author's key untouched, giving the file two
|
|
109
|
+
// declarations of one field. That is the exact drift this issue exists to
|
|
110
|
+
// remove, so it is refused rather than written.
|
|
111
|
+
const shadow = findShadowingDottedKey(parsed, segments);
|
|
112
|
+
if (shadow) {
|
|
113
|
+
throw new TomlEditError(`'${path}' is shadowed by the quoted key '${shadow}', which a dotted ` +
|
|
114
|
+
`path cannot address — edit the file directly`);
|
|
115
|
+
}
|
|
116
|
+
// Refuse anything that is not a scalar slot. Walking the PARSED document is
|
|
117
|
+
// what makes this reliable: it sees the real shape, including a value written
|
|
118
|
+
// as an inline table or reached through an array of tables.
|
|
119
|
+
let cursor = parsed;
|
|
120
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
121
|
+
const next = cursor?.[segments[i]];
|
|
122
|
+
if (next === undefined || next === null) {
|
|
123
|
+
cursor = undefined;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(next)) {
|
|
127
|
+
throw new TomlEditError(`'${path}' descends through the array '${segments
|
|
128
|
+
.slice(0, i + 1)
|
|
129
|
+
.join(".")}' — edit the file directly, config set only writes scalars`);
|
|
130
|
+
}
|
|
131
|
+
if (typeof next !== "object") {
|
|
132
|
+
throw new TomlEditError(`'${segments.slice(0, i + 1).join(".")}' is a value, not a table`);
|
|
133
|
+
}
|
|
134
|
+
cursor = next;
|
|
135
|
+
}
|
|
136
|
+
const existing = cursor === undefined ? undefined : cursor[segments[segments.length - 1]];
|
|
137
|
+
if (Array.isArray(existing)) {
|
|
138
|
+
throw new TomlEditError(`'${path}' is an array — edit the file directly, config set only writes scalars`);
|
|
139
|
+
}
|
|
140
|
+
if (existing !== undefined && existing !== null && typeof existing === "object") {
|
|
141
|
+
throw new TomlEditError(`'${path}' is a table — edit the file directly, config set only writes scalars`);
|
|
142
|
+
}
|
|
143
|
+
const key = segments[segments.length - 1];
|
|
144
|
+
const tablePath = segments.slice(0, -1).join(".");
|
|
145
|
+
const assignment = renderAssignment(key, value);
|
|
146
|
+
// Split preserving the trailing newline so a rewrite never adds or drops one.
|
|
147
|
+
const lines = text.split("\n");
|
|
148
|
+
// Find the span of the target table: from its header to the next header.
|
|
149
|
+
let start = 0;
|
|
150
|
+
let end = lines.length;
|
|
151
|
+
if (tablePath) {
|
|
152
|
+
let headerIndex = -1;
|
|
153
|
+
for (let i = 0; i < lines.length; i++) {
|
|
154
|
+
if (tableHeaderPath(lines[i]) === tablePath) {
|
|
155
|
+
headerIndex = i;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (headerIndex === -1) {
|
|
160
|
+
// No such table: append it, with the assignment under it.
|
|
161
|
+
const needsBlank = text.length > 0 && !text.endsWith("\n\n");
|
|
162
|
+
const prefix = text.endsWith("\n") ? "" : "\n";
|
|
163
|
+
return verify(text, `${text}${prefix}${needsBlank ? "\n" : ""}[${tablePath}]\n${assignment}\n`, path, value);
|
|
164
|
+
}
|
|
165
|
+
start = headerIndex + 1;
|
|
166
|
+
end = lines.length;
|
|
167
|
+
for (let i = start; i < lines.length; i++) {
|
|
168
|
+
if (tableHeaderPath(lines[i]) !== null || isArrayTableHeader(lines[i])) {
|
|
169
|
+
end = i;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Root-level keys live before the first table header.
|
|
176
|
+
for (let i = 0; i < lines.length; i++) {
|
|
177
|
+
if (tableHeaderPath(lines[i]) !== null || isArrayTableHeader(lines[i])) {
|
|
178
|
+
end = i;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (let i = start; i < end; i++) {
|
|
184
|
+
if (assignsKey(lines[i], key)) {
|
|
185
|
+
lines[i] = assignment;
|
|
186
|
+
return verify(text, lines.join("\n"), path, value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Not present: insert after the table's last non-blank line so the key lands
|
|
190
|
+
// inside the table rather than after the blank line that separates it from
|
|
191
|
+
// the next one.
|
|
192
|
+
let insertAt = end;
|
|
193
|
+
while (insertAt > start && lines[insertAt - 1].trim() === "")
|
|
194
|
+
insertAt--;
|
|
195
|
+
lines.splice(insertAt, 0, assignment);
|
|
196
|
+
return verify(text, lines.join("\n"), path, value);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Re-parse the spliced text and confirm it differs from the original in exactly
|
|
200
|
+
* one way: `path` now holds `value`.
|
|
201
|
+
*
|
|
202
|
+
* Checking only that the target path is correct is not enough, and both ways it
|
|
203
|
+
* fails were found in review. A quoted key containing a dot (`"a.b" = 1`) is
|
|
204
|
+
* unaddressable by a dotted path, so the splice appended a NEW `[a]` table —
|
|
205
|
+
* and the target check passed, because `a.b` really did hold the new value
|
|
206
|
+
* afterwards, in a second declaration the author never wrote. Separately, the
|
|
207
|
+
* line-oriented scan can match an assignment-shaped line inside a multi-line
|
|
208
|
+
* string; when the new value equals the old one the target check still passes
|
|
209
|
+
* while the string's contents have been rewritten.
|
|
210
|
+
*
|
|
211
|
+
* Comparing the WHOLE parsed document against the expected result closes both:
|
|
212
|
+
* an edit that changes anything the caller did not ask for is refused, and
|
|
213
|
+
* refusing is safe because nothing has been written yet.
|
|
214
|
+
*/
|
|
215
|
+
function verify(before, next, path, value) {
|
|
216
|
+
let parsed;
|
|
217
|
+
try {
|
|
218
|
+
parsed = parseConfigToml(next);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
throw new TomlEditError(`the edit would produce invalid TOML (${e?.message || e}) — nothing was written`);
|
|
222
|
+
}
|
|
223
|
+
const found = path
|
|
224
|
+
.split(".")
|
|
225
|
+
.reduce((cursor, segment) => cursor?.[segment], parsed);
|
|
226
|
+
if (found !== value) {
|
|
227
|
+
throw new TomlEditError(`the edit did not take effect at '${path}' — nothing was written`);
|
|
228
|
+
}
|
|
229
|
+
const expected = withScalarAt(parseConfigToml(before), path, value);
|
|
230
|
+
if (canonicalJsonStringify(parsed) !== canonicalJsonStringify(expected)) {
|
|
231
|
+
throw new TomlEditError(`the edit would change more of the file than '${path}' — nothing was ` +
|
|
232
|
+
`written. This happens when the key is written in a form a dotted path ` +
|
|
233
|
+
`cannot address (a quoted key containing '.'), or when the assignment ` +
|
|
234
|
+
`also appears inside a multi-line string. Edit the file directly.`);
|
|
235
|
+
}
|
|
236
|
+
return next;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The dotted-path spelling of a key that already covers `segments` but is
|
|
240
|
+
* written as a single quoted key, or `null` when none does.
|
|
241
|
+
*
|
|
242
|
+
* Walks the tables the path descends through, checking at each level for a key
|
|
243
|
+
* that literally contains a dot and whose expansion is the requested path or a
|
|
244
|
+
* prefix of it.
|
|
245
|
+
*/
|
|
246
|
+
function findShadowingDottedKey(document, segments) {
|
|
247
|
+
const path = segments.join(".");
|
|
248
|
+
let cursor = document;
|
|
249
|
+
const prefix = [];
|
|
250
|
+
for (let depth = 0; depth <= segments.length; depth++) {
|
|
251
|
+
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor))
|
|
252
|
+
break;
|
|
253
|
+
for (const key of Object.keys(cursor)) {
|
|
254
|
+
if (!key.includes("."))
|
|
255
|
+
continue;
|
|
256
|
+
const expanded = [...prefix, key].join(".");
|
|
257
|
+
if (path === expanded || path.startsWith(`${expanded}.`))
|
|
258
|
+
return expanded;
|
|
259
|
+
}
|
|
260
|
+
if (depth === segments.length)
|
|
261
|
+
break;
|
|
262
|
+
prefix.push(segments[depth]);
|
|
263
|
+
cursor = cursor[segments[depth]];
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
/** A deep copy of `document` with `path` set to `value`, creating tables as needed. */
|
|
268
|
+
function withScalarAt(document, path, value) {
|
|
269
|
+
const copy = structuredClone(document);
|
|
270
|
+
const segments = path.split(".");
|
|
271
|
+
let cursor = copy;
|
|
272
|
+
for (const segment of segments.slice(0, -1)) {
|
|
273
|
+
if (cursor[segment] === undefined ||
|
|
274
|
+
cursor[segment] === null ||
|
|
275
|
+
typeof cursor[segment] !== "object") {
|
|
276
|
+
cursor[segment] = {};
|
|
277
|
+
}
|
|
278
|
+
cursor = cursor[segment];
|
|
279
|
+
}
|
|
280
|
+
cursor[segments[segments.length - 1]] = value;
|
|
281
|
+
return copy;
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=toml-scalar-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toml-scalar-edit.js","sourceRoot":"","sources":["../../../src/lib/toml-scalar-edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the user selector for a list/inspection command (issue #2357).
|
|
3
|
+
*
|
|
4
|
+
* `--user-id <id>` is the canonical spelling across the CLI's list and
|
|
5
|
+
* inspection selectors — `documents list`, `documents export-all`, `workflows
|
|
6
|
+
* runs list` and `analytics` all use it, and it matches the `<user-id>`
|
|
7
|
+
* argument metavar used by `groups`, `collections` and `databases permissions`.
|
|
8
|
+
* `connections list`, `sessions list` and `tokens list` originally shipped
|
|
9
|
+
* `--user`, so that spelling stays accepted as a hidden alias: it resolves to
|
|
10
|
+
* the same value and prints one deprecation line on **stderr** (never
|
|
11
|
+
* suppressed under `--json`, because stderr cannot corrupt stdout and a
|
|
12
|
+
* scripted caller is exactly who needs to see it).
|
|
13
|
+
*
|
|
14
|
+
* Supplying both spellings exits non-zero rather than applying a silent
|
|
15
|
+
* precedence.
|
|
16
|
+
*
|
|
17
|
+
* Note: the write-side `--user` flags (`tokens create`, `tokens inject`) and
|
|
18
|
+
* the diagnostic `rule-sets debug --user` are deliberately out of scope — this
|
|
19
|
+
* is the list/inspection selector convention, not a CLI-wide rename.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveUserSelector(options: {
|
|
22
|
+
user?: string;
|
|
23
|
+
userId?: string;
|
|
24
|
+
}, commandLabel: string): string | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { error, warn } from "./output.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the user selector for a list/inspection command (issue #2357).
|
|
4
|
+
*
|
|
5
|
+
* `--user-id <id>` is the canonical spelling across the CLI's list and
|
|
6
|
+
* inspection selectors — `documents list`, `documents export-all`, `workflows
|
|
7
|
+
* runs list` and `analytics` all use it, and it matches the `<user-id>`
|
|
8
|
+
* argument metavar used by `groups`, `collections` and `databases permissions`.
|
|
9
|
+
* `connections list`, `sessions list` and `tokens list` originally shipped
|
|
10
|
+
* `--user`, so that spelling stays accepted as a hidden alias: it resolves to
|
|
11
|
+
* the same value and prints one deprecation line on **stderr** (never
|
|
12
|
+
* suppressed under `--json`, because stderr cannot corrupt stdout and a
|
|
13
|
+
* scripted caller is exactly who needs to see it).
|
|
14
|
+
*
|
|
15
|
+
* Supplying both spellings exits non-zero rather than applying a silent
|
|
16
|
+
* precedence.
|
|
17
|
+
*
|
|
18
|
+
* Note: the write-side `--user` flags (`tokens create`, `tokens inject`) and
|
|
19
|
+
* the diagnostic `rule-sets debug --user` are deliberately out of scope — this
|
|
20
|
+
* is the list/inspection selector convention, not a CLI-wide rename.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveUserSelector(options, commandLabel) {
|
|
23
|
+
if (options.user && options.userId) {
|
|
24
|
+
error(`Provide only one of --user-id or --user (--user is a deprecated alias of --user-id).`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (options.user) {
|
|
28
|
+
warn(`--user is deprecated for \`${commandLabel}\`; use --user-id instead.`);
|
|
29
|
+
return options.user;
|
|
30
|
+
}
|
|
31
|
+
return options.userId;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=user-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-selector.js","sourceRoot":"","sources":["../../../src/lib/user-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA2C,EAC3C,YAAoB;IAEpB,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnC,KAAK,CACH,sFAAsF,CACvF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,IAAI,CACF,8BAA8B,YAAY,4BAA4B,CACvE,CAAC;QACF,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC"}
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
import { type GlobalInstall, type InstallKind } from "./package-manager.js";
|
|
2
|
+
/**
|
|
3
|
+
* Renders the update notice. Pure — no network, no filesystem — so the exact
|
|
4
|
+
* wording is assertable in unit tests.
|
|
5
|
+
*
|
|
6
|
+
* `command` is null for installs we can't upgrade (a source checkout); the
|
|
7
|
+
* notice then prints the version line only.
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatUpdateNotice(opts: {
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
latestVersion: string;
|
|
12
|
+
command: string | null;
|
|
13
|
+
}): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Renders a one-line note about *other* global copies of the CLI, or null when
|
|
16
|
+
* there is nothing to warn about. Pure.
|
|
17
|
+
*
|
|
18
|
+
* Only a global install can have a duplicate: the warning tells the user to
|
|
19
|
+
* remove a global copy, which is wrong advice for anyone running the CLI from
|
|
20
|
+
* a source checkout, a project dependency, or `npx` — for them the one global
|
|
21
|
+
* copy on the machine is not a duplicate, it is their install. So the notice
|
|
22
|
+
* requires the running copy to be global itself, and to be identifiable (a
|
|
23
|
+
* null `runningPath` would make every global copy look like "another" one).
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatDuplicateWarning(globals: GlobalInstall[], runningPath: string | null, runningInstall: InstallKind | null): string | null;
|
|
1
26
|
/**
|
|
2
27
|
* Checks if a newer version of the CLI is available on npm.
|
|
3
28
|
*
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
4
5
|
import chalk from "chalk";
|
|
6
|
+
import { detectInstall, findDuplicateGlobalInstalls, normalize as normalizePath, upgradeCommand, PRIMITIVE_CLI_PACKAGE, } from "./package-manager.js";
|
|
5
7
|
const CONFIG_DIR = process.env.PRIMITIVE_CONFIG_DIR || join(homedir(), ".primitive");
|
|
6
8
|
const VERSION_CHECK_FILE = join(CONFIG_DIR, "version-check.json");
|
|
7
9
|
// Cache the result for 4 hours — short enough that users see new
|
|
8
10
|
// releases the same day they drop, long enough that chatty commands
|
|
9
|
-
// (
|
|
11
|
+
// (config push, tests in loops, etc.) don't hammer the npm registry.
|
|
10
12
|
const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000;
|
|
11
|
-
const NPM_PACKAGE_NAME =
|
|
13
|
+
const NPM_PACKAGE_NAME = PRIMITIVE_CLI_PACKAGE;
|
|
12
14
|
const FETCH_TIMEOUT_MS = 3000;
|
|
13
15
|
function ensureConfigDir() {
|
|
14
16
|
if (!existsSync(CONFIG_DIR)) {
|
|
@@ -124,6 +126,68 @@ async function fetchLatestVersion(distTag) {
|
|
|
124
126
|
return null;
|
|
125
127
|
}
|
|
126
128
|
}
|
|
129
|
+
/** The realpath of this file, or null if it can't be resolved. */
|
|
130
|
+
function selfPath() {
|
|
131
|
+
try {
|
|
132
|
+
return realpathSync(fileURLToPath(import.meta.url));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Renders the update notice. Pure — no network, no filesystem — so the exact
|
|
140
|
+
* wording is assertable in unit tests.
|
|
141
|
+
*
|
|
142
|
+
* `command` is null for installs we can't upgrade (a source checkout); the
|
|
143
|
+
* notice then prints the version line only.
|
|
144
|
+
*/
|
|
145
|
+
export function formatUpdateNotice(opts) {
|
|
146
|
+
const lines = [
|
|
147
|
+
chalk.yellow(`Update available: ${chalk.dim(opts.currentVersion)} -> ${chalk.green.bold(opts.latestVersion)}`),
|
|
148
|
+
];
|
|
149
|
+
if (opts.command) {
|
|
150
|
+
lines.push(chalk.yellow(`Run ${chalk.cyan.bold(opts.command)} to update`));
|
|
151
|
+
}
|
|
152
|
+
return lines;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* True when `runningPath` (the resolved path of the running CLI file) sits
|
|
156
|
+
* inside `install`. Both the install's own location and its symlink-resolved
|
|
157
|
+
* location are checked: pnpm's global `node_modules/<pkg>` is a link into the
|
|
158
|
+
* virtual store, so a pnpm user's running path only ever matches `realPath`.
|
|
159
|
+
*/
|
|
160
|
+
function isRunningInstall(install, runningPath) {
|
|
161
|
+
// Case-insensitive: `classifyInstallPath` lowercases, and on Windows the
|
|
162
|
+
// discovered install path and the running path can differ only in case.
|
|
163
|
+
const running = normalizePath(runningPath).toLowerCase();
|
|
164
|
+
return [install.path, install.realPath].some((candidate) => {
|
|
165
|
+
if (!candidate)
|
|
166
|
+
return false;
|
|
167
|
+
const base = normalizePath(candidate).toLowerCase();
|
|
168
|
+
return running === base || running.startsWith(`${base}/`);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Renders a one-line note about *other* global copies of the CLI, or null when
|
|
173
|
+
* there is nothing to warn about. Pure.
|
|
174
|
+
*
|
|
175
|
+
* Only a global install can have a duplicate: the warning tells the user to
|
|
176
|
+
* remove a global copy, which is wrong advice for anyone running the CLI from
|
|
177
|
+
* a source checkout, a project dependency, or `npx` — for them the one global
|
|
178
|
+
* copy on the machine is not a duplicate, it is their install. So the notice
|
|
179
|
+
* requires the running copy to be global itself, and to be identifiable (a
|
|
180
|
+
* null `runningPath` would make every global copy look like "another" one).
|
|
181
|
+
*/
|
|
182
|
+
export function formatDuplicateWarning(globals, runningPath, runningInstall) {
|
|
183
|
+
if (!runningPath || runningInstall?.kind !== "global")
|
|
184
|
+
return null;
|
|
185
|
+
const others = globals.filter((g) => !isRunningInstall(g, runningPath));
|
|
186
|
+
if (others.length === 0)
|
|
187
|
+
return null;
|
|
188
|
+
const described = others.map((g) => `${g.manager} (${g.path})`).join(", ");
|
|
189
|
+
return chalk.dim(`Another global copy of ${NPM_PACKAGE_NAME} is installed by ${described} — remove it to avoid running the wrong version.`);
|
|
190
|
+
}
|
|
127
191
|
/**
|
|
128
192
|
* Checks if a newer version of the CLI is available on npm.
|
|
129
193
|
*
|
|
@@ -157,12 +221,17 @@ export async function checkForUpdate(currentVersion) {
|
|
|
157
221
|
}
|
|
158
222
|
}
|
|
159
223
|
if (latestVersion && compareVersions(currentVersion, latestVersion) < 0) {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
: `npm i -g ${NPM_PACKAGE_NAME}`;
|
|
224
|
+
const install = detectInstall();
|
|
225
|
+
const command = upgradeCommand(install, NPM_PACKAGE_NAME, distTag);
|
|
163
226
|
console.error();
|
|
164
|
-
|
|
165
|
-
|
|
227
|
+
for (const line of formatUpdateNotice({ currentVersion, latestVersion, command })) {
|
|
228
|
+
console.error(line);
|
|
229
|
+
}
|
|
230
|
+
// Only alongside an update notice: if another package manager also owns
|
|
231
|
+
// a global copy, say where it is so the user can remove the duplicate.
|
|
232
|
+
const duplicateWarning = formatDuplicateWarning(findDuplicateGlobalInstalls(NPM_PACKAGE_NAME), selfPath(), install);
|
|
233
|
+
if (duplicateWarning)
|
|
234
|
+
console.error(duplicateWarning);
|
|
166
235
|
}
|
|
167
236
|
}
|
|
168
237
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../../src/lib/version-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../../src/lib/version-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared poll-based tailing helpers for `--watch` / `--follow` (#1969).
|
|
3
|
+
*
|
|
4
|
+
* Two render modes over one abortable poll loop:
|
|
5
|
+
* - `--watch` → periodic full-snapshot redraw (a repeated `list`/`get`).
|
|
6
|
+
* - `--follow` → append-only tail: fetch rows after an opaque server-owned
|
|
7
|
+
* `resumeAfter` checkpoint each tick.
|
|
8
|
+
*
|
|
9
|
+
* The loop is endpoint-agnostic. `--follow` consumers supply a {@link FollowSource}
|
|
10
|
+
* that knows how to fetch the page after a checkpoint; `--watch` consumers supply
|
|
11
|
+
* a `snapshot()` and a renderer. The renderer is injected (there is no `json`
|
|
12
|
+
* flag in the options) so this file never decides output shape or clears the
|
|
13
|
+
* terminal — that is entirely the caller's renderer.
|
|
14
|
+
*
|
|
15
|
+
* `--follow` is near-lossless observed-version tailing: it re-emits a row when a
|
|
16
|
+
* newer version is observed, holds no per-item dedup state, and asserts only
|
|
17
|
+
* forward progress — never a total-order or exactly-once property (the
|
|
18
|
+
* underlying GSI sorts by `modifiedAt` alone and is eventually consistent). See
|
|
19
|
+
* the design in #1969.
|
|
20
|
+
*
|
|
21
|
+
* Unit-tested against fake sources (the `cli/src/lib/paginate.ts` pattern) with
|
|
22
|
+
* no server.
|
|
23
|
+
*/
|
|
24
|
+
/** Interval floor: reject anything below this (prevents a request storm). */
|
|
25
|
+
export declare const MIN_INTERVAL_SECONDS = 1;
|
|
26
|
+
/** Default poll interval when `--interval` is omitted. */
|
|
27
|
+
export declare const DEFAULT_INTERVAL_SECONDS = 2;
|
|
28
|
+
/**
|
|
29
|
+
* Default cap on how many pages `followLoop` drains within a single tick when a
|
|
30
|
+
* burst is larger than one page. Bounds the within-tick work and, with the
|
|
31
|
+
* non-advancing-checkpoint guard, prevents an infinite drain.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_MAX_PAGES_PER_TICK = 20;
|
|
34
|
+
/**
|
|
35
|
+
* Validate an `--interval` value (in seconds). Returns the interval in seconds,
|
|
36
|
+
* or throws with a clear message for a missing/NaN/sub-floor value. Command
|
|
37
|
+
* handlers call this and surface the error; the library loops take a validated
|
|
38
|
+
* `intervalMs`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseIntervalSeconds(raw: string | undefined): number;
|
|
41
|
+
/**
|
|
42
|
+
* Sleep for `ms`, resolving early (never rejecting) if `signal` aborts. The
|
|
43
|
+
* timer and abort listener are always cleaned up, so a resolved delay leaves no
|
|
44
|
+
* dangling timer. Used by every poll loop so Ctrl-C stops promptly between
|
|
45
|
+
* ticks.
|
|
46
|
+
*/
|
|
47
|
+
export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
|
|
48
|
+
/** Error policy for the poll loops: keep polling, or throw out of the loop. */
|
|
49
|
+
export type PollErrorPolicy = "continue" | "throw";
|
|
50
|
+
export interface PollUntilOptions<T> {
|
|
51
|
+
/** Validated poll interval in milliseconds. */
|
|
52
|
+
intervalMs: number;
|
|
53
|
+
/** Abort signal (Ctrl-C at the command level). */
|
|
54
|
+
signal: AbortSignal;
|
|
55
|
+
/** What to do when `probe` throws. Default `"throw"`. */
|
|
56
|
+
onError?: PollErrorPolicy;
|
|
57
|
+
/** Called with each probed value (e.g. to report progress). */
|
|
58
|
+
onValue?: (value: T) => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Delay-then-probe until `done(value)` is true or the signal aborts. Returns
|
|
62
|
+
* the last probed value. Delay-first (not probe-first) so a caller that already
|
|
63
|
+
* holds a fresh value — the batch-status loops — keeps its "poll every N
|
|
64
|
+
* seconds" cadence with no extra immediate request.
|
|
65
|
+
*/
|
|
66
|
+
export declare function pollUntil<T>(probe: () => Promise<T>, done: (value: T) => boolean, options: PollUntilOptions<T>): Promise<T>;
|
|
67
|
+
/** One page of follow results, carrying the opaque next checkpoint. */
|
|
68
|
+
export interface FollowPage<T> {
|
|
69
|
+
items: T[];
|
|
70
|
+
/** More rows remain past this page right now (drives the within-tick drain). */
|
|
71
|
+
hasMore: boolean;
|
|
72
|
+
/** Opaque server-owned checkpoint; always present, even on an empty page. */
|
|
73
|
+
resumeAfter: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A source of follow pages. `fetchAfter(undefined)` is the bootstrap poll — it
|
|
77
|
+
* establishes the baseline checkpoint and its items are NOT emitted (tail
|
|
78
|
+
* semantics: "show me what happens from now"). `fetchAfter(<token>)` returns the
|
|
79
|
+
* bounded page after that checkpoint.
|
|
80
|
+
*/
|
|
81
|
+
export interface FollowSource<T> {
|
|
82
|
+
fetchAfter: (resumeAfter: string | undefined) => Promise<FollowPage<T>>;
|
|
83
|
+
/**
|
|
84
|
+
* Max pages to drain within one tick when a burst exceeds one page. Defaults
|
|
85
|
+
* to {@link DEFAULT_MAX_PAGES_PER_TICK}. The drain also stops early if the
|
|
86
|
+
* checkpoint fails to advance (non-advancing-checkpoint guard).
|
|
87
|
+
*/
|
|
88
|
+
maxPagesPerTick?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface FollowOptions<T> {
|
|
91
|
+
intervalMs: number;
|
|
92
|
+
signal: AbortSignal;
|
|
93
|
+
/** What to do when a poll throws. Default `"continue"` (keep tailing). */
|
|
94
|
+
onError?: PollErrorPolicy;
|
|
95
|
+
/** Render newly-observed rows (server-ascending; no client sort). */
|
|
96
|
+
render: (items: T[]) => void;
|
|
97
|
+
}
|
|
98
|
+
export interface WatchOptions<T> {
|
|
99
|
+
intervalMs: number;
|
|
100
|
+
signal: AbortSignal;
|
|
101
|
+
/** What to do when a poll throws. Default `"continue"`. */
|
|
102
|
+
onError?: PollErrorPolicy;
|
|
103
|
+
/**
|
|
104
|
+
* Render the full snapshot. The renderer owns any clearing/redraw — the loop
|
|
105
|
+
* never clears the terminal itself.
|
|
106
|
+
*/
|
|
107
|
+
render: (items: T[]) => void;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Append-only tail. Bootstraps the baseline checkpoint (emitting nothing), then
|
|
111
|
+
* each tick drains forward while `hasMore` (bounded by `maxPagesPerTick`,
|
|
112
|
+
* stopping if the checkpoint fails to advance), rendering rows server-ascending.
|
|
113
|
+
* Holds no per-item state — the only carried state is the single opaque
|
|
114
|
+
* `resumeAfter` string.
|
|
115
|
+
*/
|
|
116
|
+
export declare function followLoop<T>(source: FollowSource<T>, options: FollowOptions<T>): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Snapshot redraw. Each tick fetches the full current snapshot and hands it to
|
|
119
|
+
* the renderer, which owns any clearing/redraw. No checkpoint, no dedup.
|
|
120
|
+
*/
|
|
121
|
+
export declare function watchLoop<T>(snapshot: () => Promise<T[]>, options: WatchOptions<T>): Promise<void>;
|