primitive-admin 1.0.62 → 1.0.64
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 +132 -42
- package/assets/skill/skills/primitive-platform/SKILL.md +142 -661
- package/dist/bin/primitive.js +121 -41
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.js +37 -50
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +178 -9
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.js +17 -7
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth-sessions.d.ts +7 -0
- package/dist/src/commands/auth-sessions.js +144 -0
- package/dist/src/commands/auth-sessions.js.map +1 -0
- package/dist/src/commands/auth.js +76 -116
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +2 -1
- package/dist/src/commands/blob-buckets.js.map +1 -1
- package/dist/src/commands/catalog.js +26 -10
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collection-type-configs.js +3 -10
- package/dist/src/commands/collection-type-configs.js.map +1 -1
- package/dist/src/commands/collections.js +736 -19
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/config.js +13 -27
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/connections.js +8 -9
- package/dist/src/commands/connections.js.map +1 -1
- package/dist/src/commands/cron-triggers.js +2 -1
- package/dist/src/commands/cron-triggers.js.map +1 -1
- package/dist/src/commands/database-type-configs.js +5 -12
- package/dist/src/commands/database-type-configs.js.map +1 -1
- package/dist/src/commands/databases.js +59 -72
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +58 -0
- package/dist/src/commands/documents.js +1623 -95
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.js +5 -4
- package/dist/src/commands/email-templates.js.map +1 -1
- package/dist/src/commands/env.d.ts +1 -1
- package/dist/src/commands/env.js +106 -44
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/feature-flags.js +2 -1
- package/dist/src/commands/feature-flags.js.map +1 -1
- package/dist/src/commands/functions.d.ts +20 -0
- package/dist/src/commands/functions.js +1630 -0
- package/dist/src/commands/functions.js.map +1 -0
- package/dist/src/commands/group-type-configs.js +2 -1
- package/dist/src/commands/group-type-configs.js.map +1 -1
- package/dist/src/commands/groups.js +30 -17
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.js +17 -11
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/init.d.ts +24 -12
- package/dist/src/commands/init.js +198 -110
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +65 -26
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/locks.js +17 -2
- package/dist/src/commands/locks.js.map +1 -1
- package/dist/src/commands/metadata-category-configs.js +2 -1
- package/dist/src/commands/metadata-category-configs.js.map +1 -1
- package/dist/src/commands/metadata.js +8 -1
- package/dist/src/commands/metadata.js.map +1 -1
- package/dist/src/commands/prompts.js +106 -59
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.js +3 -10
- 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 +159 -25
- package/dist/src/commands/scripts.js.map +1 -1
- package/dist/src/commands/secrets.js +2 -1
- package/dist/src/commands/secrets.js.map +1 -1
- package/dist/src/commands/sessions.js +10 -9
- package/dist/src/commands/sessions.js.map +1 -1
- package/dist/src/commands/sync-app-settings.js +3 -5
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +391 -44
- package/dist/src/commands/sync.js +2622 -534
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.js +2 -1
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.js +19 -10
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/vars.js +2 -1
- package/dist/src/commands/vars.js.map +1 -1
- package/dist/src/commands/waitlist.js +2 -1
- package/dist/src/commands/waitlist.js.map +1 -1
- package/dist/src/commands/webhooks.d.ts +29 -0
- package/dist/src/commands/webhooks.js +135 -64
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.d.ts +1 -29
- package/dist/src/commands/workflows.js +113 -179
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +628 -14
- package/dist/src/lib/api-client.js +776 -70
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +1 -1
- package/dist/src/lib/app-settings-descriptor.js +9 -1
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/block-selector.d.ts +58 -0
- package/dist/src/lib/block-selector.js +92 -0
- package/dist/src/lib/block-selector.js.map +1 -0
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +24 -54
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +31 -153
- package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
- package/dist/src/lib/collection-export.d.ts +184 -0
- package/dist/src/lib/collection-export.js +252 -0
- package/dist/src/lib/collection-export.js.map +1 -0
- package/dist/src/lib/config-json-field.d.ts +28 -0
- package/dist/src/lib/config-json-field.js +56 -0
- package/dist/src/lib/config-json-field.js.map +1 -0
- package/dist/src/lib/config-object-descriptor.js +88 -6
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/config-payload.d.ts +8 -1
- package/dist/src/lib/config-payload.js +49 -4
- package/dist/src/lib/config-payload.js.map +1 -1
- package/dist/src/lib/config-surface.d.ts +12 -1
- package/dist/src/lib/config-surface.js +71 -3
- package/dist/src/lib/config-surface.js.map +1 -1
- package/dist/src/lib/config.d.ts +31 -13
- package/dist/src/lib/config.js +34 -70
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/credentials-store.d.ts +37 -22
- package/dist/src/lib/credentials-store.js +103 -97
- package/dist/src/lib/credentials-store.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTypeIR.js +8 -0
- package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -1
- package/dist/src/lib/deprecation.d.ts +22 -0
- package/dist/src/lib/deprecation.js +43 -0
- package/dist/src/lib/deprecation.js.map +1 -0
- package/dist/src/lib/document-export-permissions.d.ts +30 -0
- package/dist/src/lib/document-export-permissions.js +54 -0
- package/dist/src/lib/document-export-permissions.js.map +1 -0
- 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-rows.d.ts +137 -0
- package/dist/src/lib/document-ingest-rows.js +181 -0
- package/dist/src/lib/document-ingest-rows.js.map +1 -0
- package/dist/src/lib/document-ingest.d.ts +101 -0
- package/dist/src/lib/document-ingest.js +384 -0
- package/dist/src/lib/document-ingest.js.map +1 -0
- package/dist/src/lib/env-resolver-core.d.ts +128 -17
- package/dist/src/lib/env-resolver-core.js +204 -22
- package/dist/src/lib/env-resolver-core.js.map +1 -1
- package/dist/src/lib/env-resolver.d.ts +18 -3
- package/dist/src/lib/env-resolver.js +23 -3
- package/dist/src/lib/env-resolver.js.map +1 -1
- package/dist/src/lib/function-bundle.d.ts +147 -0
- package/dist/src/lib/function-bundle.js +341 -0
- package/dist/src/lib/function-bundle.js.map +1 -0
- package/dist/src/lib/function-collect.d.ts +123 -0
- package/dist/src/lib/function-collect.js +610 -0
- package/dist/src/lib/function-collect.js.map +1 -0
- package/dist/src/lib/function-db-types.d.ts +202 -0
- package/dist/src/lib/function-db-types.js +870 -0
- package/dist/src/lib/function-db-types.js.map +1 -0
- package/dist/src/lib/function-document-types.d.ts +144 -0
- package/dist/src/lib/function-document-types.js +370 -0
- package/dist/src/lib/function-document-types.js.map +1 -0
- package/dist/src/lib/function-grants-preflight.d.ts +64 -0
- package/dist/src/lib/function-grants-preflight.js +105 -0
- package/dist/src/lib/function-grants-preflight.js.map +1 -0
- 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-log-tail.d.ts +132 -0
- package/dist/src/lib/function-log-tail.js +262 -0
- package/dist/src/lib/function-log-tail.js.map +1 -0
- package/dist/src/lib/function-run.d.ts +289 -0
- package/dist/src/lib/function-run.js +389 -0
- package/dist/src/lib/function-run.js.map +1 -0
- package/dist/src/lib/function-schema-codegen.d.ts +143 -0
- package/dist/src/lib/function-schema-codegen.js +420 -0
- package/dist/src/lib/function-schema-codegen.js.map +1 -0
- package/dist/src/lib/function-sync.d.ts +459 -0
- package/dist/src/lib/function-sync.js +1258 -0
- package/dist/src/lib/function-sync.js.map +1 -0
- package/dist/src/lib/function-trigger-listing.d.ts +27 -0
- package/dist/src/lib/function-trigger-listing.js +70 -0
- package/dist/src/lib/function-trigger-listing.js.map +1 -0
- package/dist/src/lib/function-typecheck.d.ts +86 -0
- package/dist/src/lib/function-typecheck.js +370 -0
- package/dist/src/lib/function-typecheck.js.map +1 -0
- 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-allowlist.js +4 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +2332 -78
- package/dist/src/lib/generated-config-surfaces.js +6979 -468
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +12 -0
- package/dist/src/lib/generated-sdk-types.js +13 -0
- package/dist/src/lib/generated-sdk-types.js.map +1 -0
- package/dist/src/lib/init-adopt.js +2 -2
- package/dist/src/lib/init-adopt.js.map +1 -1
- package/dist/src/lib/init-assets.js +3 -3
- 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 +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 +41 -0
- package/dist/src/lib/init-ios-links.js +67 -1
- package/dist/src/lib/init-ios-links.js.map +1 -1
- package/dist/src/lib/init-plan.d.ts +1 -1
- package/dist/src/lib/init-plan.js +1 -1
- package/dist/src/lib/init-xcode.d.ts +6 -5
- package/dist/src/lib/init-xcode.js +34 -10
- package/dist/src/lib/init-xcode.js.map +1 -1
- 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/ios-app-id.d.ts +34 -0
- package/dist/src/lib/ios-app-id.js +69 -0
- package/dist/src/lib/ios-app-id.js.map +1 -0
- package/dist/src/lib/list-options.d.ts +68 -0
- package/dist/src/lib/list-options.js +89 -0
- package/dist/src/lib/list-options.js.map +1 -0
- package/dist/src/lib/local-state.d.ts +1 -1
- package/dist/src/lib/local-state.js +1 -1
- package/dist/src/lib/local-test-cases.d.ts +63 -0
- package/dist/src/lib/local-test-cases.js +136 -0
- package/dist/src/lib/local-test-cases.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +152 -5
- package/dist/src/lib/log-inspection.js +179 -2
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/logout-admin-session.d.ts +33 -0
- package/dist/src/lib/logout-admin-session.js +70 -0
- package/dist/src/lib/logout-admin-session.js.map +1 -0
- 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/paginate.d.ts +15 -0
- package/dist/src/lib/paginate.js +17 -0
- package/dist/src/lib/paginate.js.map +1 -1
- package/dist/src/lib/project-config.d.ts +46 -21
- package/dist/src/lib/project-config.js +74 -47
- package/dist/src/lib/project-config.js.map +1 -1
- package/dist/src/lib/prompt-cost-format.d.ts +11 -0
- package/dist/src/lib/prompt-cost-format.js +41 -0
- package/dist/src/lib/prompt-cost-format.js.map +1 -0
- package/dist/src/lib/prompt-schema-codegen.d.ts +147 -0
- package/dist/src/lib/prompt-schema-codegen.js +462 -0
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
- package/dist/src/lib/refresh-admin-credentials.d.ts +9 -1
- package/dist/src/lib/refresh-admin-credentials.js +22 -2
- package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
- package/dist/src/lib/resolve-owner.d.ts +19 -0
- package/dist/src/lib/resolve-owner.js +20 -0
- package/dist/src/lib/resolve-owner.js.map +1 -0
- package/dist/src/lib/skill-installer.d.ts +49 -3
- package/dist/src/lib/skill-installer.js +275 -100
- package/dist/src/lib/skill-installer.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/snapshot-build.d.ts +50 -0
- package/dist/src/lib/snapshot-build.js +111 -0
- package/dist/src/lib/snapshot-build.js.map +1 -0
- package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
- package/dist/src/lib/snapshot-manifest-layout.js +70 -0
- package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
- package/dist/src/lib/snapshots.d.ts +8 -9
- package/dist/src/lib/snapshots.js +20 -83
- package/dist/src/lib/snapshots.js.map +1 -1
- package/dist/src/lib/step-run-table.d.ts +43 -0
- package/dist/src/lib/step-run-table.js +129 -0
- package/dist/src/lib/step-run-table.js.map +1 -0
- package/dist/src/lib/storage-pending-retry.d.ts +26 -0
- package/dist/src/lib/storage-pending-retry.js +42 -0
- package/dist/src/lib/storage-pending-retry.js.map +1 -0
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/banners.d.ts +24 -0
- package/dist/src/lib/swift-codegen/banners.js +25 -0
- package/dist/src/lib/swift-codegen/banners.js.map +1 -0
- package/dist/src/lib/swift-codegen/dbGenerator.js +19 -7
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +139 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js +462 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/generator.d.ts +7 -1
- package/dist/src/lib/swift-codegen/generator.js +28 -11
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +15 -0
- package/dist/src/lib/swift-codegen/schemaToSwift.js +21 -4
- package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -1
- package/dist/src/lib/sync-paths.d.ts +72 -55
- package/dist/src/lib/sync-paths.js +97 -100
- package/dist/src/lib/sync-paths.js.map +1 -1
- package/dist/src/lib/sync-resource-types.d.ts +23 -4
- package/dist/src/lib/sync-resource-types.js +106 -8
- package/dist/src/lib/sync-resource-types.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/dist/src/lib/template.d.ts +2 -2
- package/dist/src/lib/template.js +2 -2
- 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 +1 -1
- package/dist/src/lib/test-case-keys.js +1 -1
- package/dist/src/lib/test-case-variables.d.ts +14 -0
- package/dist/src/lib/test-case-variables.js +42 -0
- package/dist/src/lib/test-case-variables.js.map +1 -1
- package/dist/src/lib/toml-database-config.js +19 -2
- package/dist/src/lib/toml-database-config.js.map +1 -1
- 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/webhook-deliver.d.ts +209 -0
- package/dist/src/lib/webhook-deliver.js +519 -0
- package/dist/src/lib/webhook-deliver.js.map +1 -0
- package/dist/src/lib/workflow-codegen/generator.d.ts +1 -1
- package/dist/src/lib/workflow-codegen/generator.js +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +17 -8
- package/dist/src/lib/workflow-toml-validator.js +25 -12
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/dist/src/lib/workflow-usage.d.ts +196 -0
- package/dist/src/lib/workflow-usage.js +310 -0
- package/dist/src/lib/workflow-usage.js.map +1 -0
- package/dist/src/types/index.d.ts +12 -2
- package/package.json +5 -4
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
2
2
|
import { appendFileSync, existsSync, readFileSync, readdirSync, writeFileSync, } from "fs";
|
|
3
3
|
import { basename, join, relative, resolve } from "path";
|
|
4
|
-
import { CURRENT_CONFIG_VERSION,
|
|
4
|
+
import { CURRENT_CONFIG_VERSION, PROJECT_TREE_DIR, PROJECT_CONFIG_FILENAME, PROJECT_CONFIG_DISPLAY_NAME, findProjectRoot, saveProjectConfig, loadProjectConfigFromPath, } from "../lib/project-config.js";
|
|
5
5
|
import { resolveAdoptedEnvironment } from "../lib/init-adopt.js";
|
|
6
6
|
import { CANONICAL_SCHEMA_HEADER, FALLBACK_STARTER_SCHEMA, platformLabel, rootAgentsMarkdown, } from "../lib/init-assets.js";
|
|
7
7
|
import { checkInitGuards, planInit } from "../lib/init-plan.js";
|
|
8
8
|
import { resolveProductionRuntime } from "../lib/init-production-env.js";
|
|
9
9
|
import { checkClientWiring, composeSchemaContent, declaredModelNames, findClientSchema, wireClientToSchema, writeCanonicalSchema, } from "../lib/init-schema.js";
|
|
10
10
|
import { declareSharedProjectConfig } from "../lib/init-xcode.js";
|
|
11
|
-
import { deriveCallbackScheme, linkOptInGuidance, missingUrlTypeGuidance, stampCallbackScheme, } from "../lib/init-ios-links.js";
|
|
11
|
+
import { deriveCallbackScheme, linkOptInGuidance, missingUrlTypeGuidance, stampCallbackScheme, universalLinkGuidance, } from "../lib/init-ios-links.js";
|
|
12
|
+
import { appHasBothPlatforms, detectClientPlatform, discoverClientPlatforms, } from "../lib/init-client-platforms.js";
|
|
13
|
+
import { planEmailRedirectUriMerge } from "../lib/init-email-redirect-uris.js";
|
|
12
14
|
import { getExplicitEnvName } from "../lib/env-resolver.js";
|
|
13
15
|
import { ApiClient } from "../lib/api-client.js";
|
|
14
16
|
import { browserLogin } from "../lib/auth-flow.js";
|
|
15
|
-
import { loadCredentials
|
|
17
|
+
import { loadCredentials } from "../lib/config.js";
|
|
18
|
+
import { allowGlobalCredentials } from "../lib/credentials-store.js";
|
|
16
19
|
import { isProjectMode, seedCredentialsForEnvInProject, } from "../lib/credentials-store.js";
|
|
17
20
|
import { getCurrentEnvironment, getProjectConfig } from "../lib/env-resolver.js";
|
|
18
21
|
import { normalizeServerUrl } from "../lib/channel.js";
|
|
@@ -26,30 +29,86 @@ import { divider, error, info, keyValue, success, warn, } from "../lib/output.js
|
|
|
26
29
|
import { installCommand, PRIMITIVE_CLI_PACKAGE } from "../lib/package-manager.js";
|
|
27
30
|
import { gitInitialCommit, gitInitRepo, installPnpmGlobally, installPrimitiveCli, isDirectoryEmpty, isPnpmInstalled, isPortFree, isPrimitiveCliInstalled, isXcodeInstalled, iosTemplateUrl, vueTemplateUrl, resolveTemplateBranch, prepareTemplate, runPnpmInstall, runSwiftPackageResolve, updateEnvFile, updateEnvPort, configurePrimitiveJson, updateViteConfig, } from "../lib/template.js";
|
|
28
31
|
/**
|
|
29
|
-
* The
|
|
32
|
+
* The origin a local dev server is served from. ONE spelling: the app's CORS
|
|
33
|
+
* origins, its `emailRedirectUris` seed and the environment's `webUrl` all
|
|
34
|
+
* name this, and a second spelling would be a 400 on the scaffolded app's
|
|
35
|
+
* first sign-in (#2982).
|
|
36
|
+
*/
|
|
37
|
+
export function localDevOrigin(port) {
|
|
38
|
+
return `http://localhost:${port}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Point an existing environment at the app's web counterpart.
|
|
30
42
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* - project mode: `<projectRoot>/.primitive/sync-backups/<env>/<appId>/`
|
|
35
|
-
* - legacy `--dir <p>`: `<p>/.snapshots/` (sibling of the user dir)
|
|
36
|
-
* - no-project fallback: `./config-snapshots/`
|
|
37
|
-
* The bare `.snapshots/` and `config-snapshots/` entries (no leading slash)
|
|
38
|
-
* match those directories wherever `--dir` points, so backup copies — including
|
|
39
|
-
* the restored `.primitive-sync.json` baseline — can never surface as untracked
|
|
40
|
-
* files in a tracked custom config dir.
|
|
43
|
+
* Returns false — and writes nothing — when there is no config to write, the
|
|
44
|
+
* environment is not in it, or the file cannot be read: an adopting run does
|
|
45
|
+
* not own the project config, and a seed is never worth failing an init over.
|
|
41
46
|
*/
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
export function seedEnvironmentWebUrl(projectRoot, environment, webUrl) {
|
|
48
|
+
const configPath = join(projectRoot, PROJECT_TREE_DIR, PROJECT_CONFIG_FILENAME);
|
|
49
|
+
if (!existsSync(configPath))
|
|
50
|
+
return false;
|
|
51
|
+
let config;
|
|
52
|
+
try {
|
|
53
|
+
config = loadProjectConfigFromPath(configPath);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const entry = config.environments[environment];
|
|
59
|
+
if (!entry)
|
|
60
|
+
return false;
|
|
61
|
+
if (entry.webUrl === webUrl)
|
|
62
|
+
return true;
|
|
63
|
+
entry.webUrl = webUrl;
|
|
64
|
+
saveProjectConfig(configPath, config);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Make sure this app allows the dev sign-in callback, so pointing the
|
|
69
|
+
* environment at the web client cannot break email sign-in (#2982).
|
|
70
|
+
*
|
|
71
|
+
* Returns true when the callback is on the app's `emailRedirectUris` — already
|
|
72
|
+
* there, or merged in here. Returns false when it is not and this run must not
|
|
73
|
+
* put it there: no credentials to ask the server with, an app whose allow-list
|
|
74
|
+
* would be NARROWED by writing one (see `planEmailRedirectUriMerge`), or an API
|
|
75
|
+
* call that failed. A false answer costs nothing but the automatic seed: the
|
|
76
|
+
* caller prints the manual step instead, and the app keeps working code-only.
|
|
77
|
+
*/
|
|
78
|
+
async function devCallbackIsAllowListed(appId, callbackUri, credentials) {
|
|
79
|
+
if (!credentials)
|
|
80
|
+
return false;
|
|
81
|
+
try {
|
|
82
|
+
const apiClient = new ApiClient();
|
|
83
|
+
const settings = await apiClient.getAppSettings(appId);
|
|
84
|
+
const plan = planEmailRedirectUriMerge(settings?.emailRedirectUris, callbackUri);
|
|
85
|
+
if (plan.action === "already-listed")
|
|
86
|
+
return true;
|
|
87
|
+
if (plan.action === "leave-alone") {
|
|
88
|
+
warn(`Not touching this app's sign-in link allow-list: ${plan.reason}.`);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
await apiClient.updateAppSettings(appId, { emailRedirectUris: plan.uris });
|
|
92
|
+
success(`Allow-listed the dev sign-in link: ${callbackUri}`);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
warn(`Could not check this app's sign-in link allow-list: ${err.message}`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The .gitignore lines `init` writes for Primitive CLI local state — one line,
|
|
102
|
+
* with no per-file exception (#3153, intent criterion 2).
|
|
103
|
+
*
|
|
104
|
+
* That is the whole point of the move: `primitive/` is committed (the config
|
|
105
|
+
* and every environment's TOML, including the shared `.sync-state.json`
|
|
106
|
+
* baseline) and `.primitive/` is machine-local (credentials, this machine's
|
|
107
|
+
* `primitive env use` selection, pre-pull snapshot backups). Before the move
|
|
108
|
+
* one directory meant both things, so the list needed six entries and a
|
|
109
|
+
* comment explaining which files inside it were committed anyway.
|
|
110
|
+
*/
|
|
111
|
+
export const GITIGNORE_LOCAL_STATE_ENTRIES = [".primitive/"];
|
|
53
112
|
/**
|
|
54
113
|
* Ensures the project root's .gitignore lists the Primitive CLI's local state.
|
|
55
114
|
*
|
|
@@ -58,47 +117,19 @@ export const GITIGNORE_LOCAL_STATE_ENTRIES = [
|
|
|
58
117
|
* per-machine state a new client's environment produces cannot be committed.
|
|
59
118
|
*/
|
|
60
119
|
export function ensureGitignoreLocalState(targetDir) {
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// to be committed. Only `.primitive-sync.json` (the auto-managed sync
|
|
66
|
-
// state baseline) is per-machine local state; everything else in the
|
|
67
|
-
// sync directory is the version-controllable export.
|
|
68
|
-
//
|
|
69
|
-
// Legacy note: an earlier version of init wrote `.primitive/sync/` here,
|
|
70
|
-
// which incorrectly hid the TOML export tree from `git status`. If we see
|
|
71
|
-
// that legacy entry, replace it with the targeted pattern in-place so
|
|
72
|
-
// users who re-run init pick up the fix.
|
|
120
|
+
// One rule: `.primitive/` is machine-local and never committed, `primitive/`
|
|
121
|
+
// is the committed tree. Narrower `.primitive/...` entries left over from an
|
|
122
|
+
// older init are harmless subsets of this line, so they are left alone — the
|
|
123
|
+
// migration document owns tidying an adopted project's .gitignore.
|
|
73
124
|
const gitignorePath = join(targetDir, ".gitignore");
|
|
74
|
-
const LEGACY_SYNC_IGNORE = ".primitive/sync/";
|
|
75
125
|
const gitignoreEntries = [...GITIGNORE_LOCAL_STATE_ENTRIES];
|
|
76
126
|
let linesToAdd = [];
|
|
77
127
|
if (existsSync(gitignorePath)) {
|
|
78
|
-
const existing = readFileSync(gitignorePath, "utf-8")
|
|
79
|
-
const trimmedLines = existing.split(/\r?\n/).map((l) => l.trim());
|
|
80
|
-
// Migrate the legacy `.primitive/sync/` entry to the targeted pattern,
|
|
81
|
-
// if present. Preserve original line endings/formatting elsewhere.
|
|
82
|
-
if (trimmedLines.includes(LEGACY_SYNC_IGNORE)) {
|
|
83
|
-
const rewritten = existing
|
|
84
|
-
.split(/\r?\n/)
|
|
85
|
-
.map((line) => line.trim() === LEGACY_SYNC_IGNORE
|
|
86
|
-
? ".primitive/sync/**/.primitive-sync.json"
|
|
87
|
-
: line)
|
|
88
|
-
.join("\n");
|
|
89
|
-
try {
|
|
90
|
-
writeFileSync(gitignorePath, rewritten);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
// Non-fatal
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
// Re-read after potential migration so we don't double-add entries.
|
|
97
|
-
const refreshed = readFileSync(gitignorePath, "utf-8")
|
|
128
|
+
const existing = readFileSync(gitignorePath, "utf-8")
|
|
98
129
|
.split(/\r?\n/)
|
|
99
130
|
.map((l) => l.trim());
|
|
100
131
|
for (const entry of gitignoreEntries) {
|
|
101
|
-
if (!
|
|
132
|
+
if (!existing.includes(entry)) {
|
|
102
133
|
linesToAdd.push(entry);
|
|
103
134
|
}
|
|
104
135
|
}
|
|
@@ -117,13 +148,13 @@ export function ensureGitignoreLocalState(targetDir) {
|
|
|
117
148
|
}
|
|
118
149
|
}
|
|
119
150
|
/**
|
|
120
|
-
* Writes
|
|
151
|
+
* Writes primitive/config.json into the project directory and ensures the
|
|
121
152
|
* .gitignore entry for local state exists. Safe to call in a directory that
|
|
122
153
|
* already has a config — in that case we add/update the "dev" environment
|
|
123
154
|
* instead of overwriting the file.
|
|
124
155
|
*/
|
|
125
156
|
async function scaffoldProjectConfig(opts) {
|
|
126
|
-
const configPath = join(opts.targetDir,
|
|
157
|
+
const configPath = join(opts.targetDir, PROJECT_TREE_DIR, PROJECT_CONFIG_FILENAME);
|
|
127
158
|
let config;
|
|
128
159
|
if (existsSync(configPath)) {
|
|
129
160
|
// Respect an existing config: merge in the new dev env if missing.
|
|
@@ -218,7 +249,7 @@ async function ensureAuthenticated(credentials, serverUrl, serverChanged, contex
|
|
|
218
249
|
* matches `serverUrl`, so the project-mode "not logged in" remedy can name a
|
|
219
250
|
* real, working environment instead of a placeholder. Comparison uses the
|
|
220
251
|
* same canonicalization as `resolveInitServer` (trailing-slash-insensitive)
|
|
221
|
-
* so `https://host/` in
|
|
252
|
+
* so `https://host/` in `primitive/config.json` still matches.
|
|
222
253
|
*/
|
|
223
254
|
function findEnvironmentForServer(serverUrl) {
|
|
224
255
|
const project = getProjectConfig();
|
|
@@ -294,9 +325,9 @@ Examples:
|
|
|
294
325
|
|
|
295
326
|
One app, several clients:
|
|
296
327
|
Selecting more than one platform scaffolds them side by side under one project:
|
|
297
|
-
each client in its own directory (web/, ios/), with
|
|
328
|
+
each client in its own directory (web/, ios/), with primitive/config.json, the
|
|
298
329
|
git repository and the shared models/models.toml at the root. Run inside a
|
|
299
|
-
directory that already has an ancestor
|
|
330
|
+
directory that already has an ancestor primitive/config.json and init ADDS the
|
|
300
331
|
client to that app instead: it reuses the selected environment's app, writes no
|
|
301
332
|
nested .primitive/ or .git/, makes no commit, and leaves the files for you to
|
|
302
333
|
review and commit in the outer repository.
|
|
@@ -347,6 +378,11 @@ This command will:
|
|
|
347
378
|
.action(handleInit);
|
|
348
379
|
}
|
|
349
380
|
async function handleInit(projectDirArg, options) {
|
|
381
|
+
// `init` is what CREATES a project, so by definition it runs before one
|
|
382
|
+
// exists — and it has to call the API to do it. One of the four commands
|
|
383
|
+
// permitted to reach the global credentials file with no project in scope
|
|
384
|
+
// (#3154, intent criterion 5).
|
|
385
|
+
allowGlobalCredentials();
|
|
350
386
|
// Validate an explicitly-passed `--platform` flag up front. Only `web` and
|
|
351
387
|
// `ios` are supported (#1009 removed `macos`), and the flag names a LIST —
|
|
352
388
|
// `--platform web,ios` scaffolds both clients of one app (#2890). An omitted
|
|
@@ -448,7 +484,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
448
484
|
const defaultAppName = projectDir === "." ? basename(targetDir) : basename(projectDir);
|
|
449
485
|
// ── Which layout is this run producing? (#2890) ────────────────────────
|
|
450
486
|
//
|
|
451
|
-
// A
|
|
487
|
+
// A `primitive/config.json` in an ANCESTOR of the target means this app
|
|
452
488
|
// already exists and this run adds a client to it. Passing the ancestor only
|
|
453
489
|
// when it differs from the target keeps `primitive init .` at a project root
|
|
454
490
|
// on its existing merge behavior.
|
|
@@ -633,7 +669,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
633
669
|
}
|
|
634
670
|
else {
|
|
635
671
|
error(`This project has no environment configured for ${serverUrl}. ` +
|
|
636
|
-
`Add one (\`primitive env add <name> --api-url ${serverUrl}
|
|
672
|
+
`Add one (\`primitive env add <name> --api-url ${serverUrl} --app-id <app-id>\`) and ` +
|
|
637
673
|
`re-run with \`-e <name>${serverFlag}\`, or re-run init outside the project directory.`);
|
|
638
674
|
}
|
|
639
675
|
}
|
|
@@ -699,6 +735,14 @@ async function handleInit(projectDirArg, options) {
|
|
|
699
735
|
let credentials = existingCredentials;
|
|
700
736
|
let appId;
|
|
701
737
|
let appName;
|
|
738
|
+
/**
|
|
739
|
+
* Whether THIS run created the app. A created app is seeded with
|
|
740
|
+
* `http://localhost:5173/oauth/callback` in `emailRedirectUris`; an adopted
|
|
741
|
+
* one carries whatever allow-list it already had, which is why the combined
|
|
742
|
+
* app's `webUrl` seed asks the server before pointing the sign-in link at a
|
|
743
|
+
* dev callback (#2982).
|
|
744
|
+
*/
|
|
745
|
+
let appWasCreatedByThisRun = false;
|
|
702
746
|
const preparedTemplates = new Map();
|
|
703
747
|
const cleanupPreparedTemplate = () => {
|
|
704
748
|
for (const prepared of preparedTemplates.values()) {
|
|
@@ -847,6 +891,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
847
891
|
throw new Error("App created but no appId returned");
|
|
848
892
|
}
|
|
849
893
|
appId = id;
|
|
894
|
+
appWasCreatedByThisRun = true;
|
|
850
895
|
success(`App created: ${appName}`);
|
|
851
896
|
keyValue("App ID", appId);
|
|
852
897
|
}
|
|
@@ -1024,20 +1069,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1024
1069
|
// Non-fatal: git may not be installed, or already a git repo
|
|
1025
1070
|
}
|
|
1026
1071
|
}
|
|
1027
|
-
// Step 4.
|
|
1028
|
-
// already the project's, and the CLI's global selection is not this run's to
|
|
1029
|
-
// move.
|
|
1030
|
-
if (plan.ownsApp) {
|
|
1031
|
-
try {
|
|
1032
|
-
setCurrentApp(appId, appName);
|
|
1033
|
-
info(`CLI now using app: ${appName} (${appId})`);
|
|
1034
|
-
}
|
|
1035
|
-
catch (err) {
|
|
1036
|
-
// Not critical - user may not be logged in (e.g. used --use-existing-app-id without login)
|
|
1037
|
-
warn(`Could not set current app in CLI config: ${err.message}`);
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
// Step 4.6: Scaffold .primitive/config.json with a "dev" environment bound
|
|
1072
|
+
// Step 4.6: Scaffold primitive/config.json with a "dev" environment bound
|
|
1041
1073
|
// to this app, and seed its credential slot with the session we just
|
|
1042
1074
|
// authenticated — so `primitive` commands run from inside the project pick
|
|
1043
1075
|
// up project mode without a second login.
|
|
@@ -1052,10 +1084,10 @@ async function handleInit(projectDirArg, options) {
|
|
|
1052
1084
|
if (credentials) {
|
|
1053
1085
|
seedCredentialsForEnvInProject(plan.projectRoot, "dev", credentials);
|
|
1054
1086
|
}
|
|
1055
|
-
success(`Project environment "dev" configured (
|
|
1087
|
+
success(`Project environment "dev" configured (primitive/config.json).`);
|
|
1056
1088
|
}
|
|
1057
1089
|
catch (err) {
|
|
1058
|
-
warn(`Could not scaffold
|
|
1090
|
+
warn(`Could not scaffold primitive/config.json: ${err.message}`);
|
|
1059
1091
|
}
|
|
1060
1092
|
}
|
|
1061
1093
|
else {
|
|
@@ -1117,6 +1149,23 @@ async function handleInit(projectDirArg, options) {
|
|
|
1117
1149
|
* candidate scheme rather than a registered one.
|
|
1118
1150
|
*/
|
|
1119
1151
|
const iosCallbackSchemes = [];
|
|
1152
|
+
/**
|
|
1153
|
+
* An app with BOTH clients emails ONE https sign-in link — the web app's
|
|
1154
|
+
* callback, which opens the iOS app where it is installed (#2982). "Both"
|
|
1155
|
+
* counts the clients already in the repository, including one that IS the
|
|
1156
|
+
* project root (the flat single-client layout gaining its second platform).
|
|
1157
|
+
*/
|
|
1158
|
+
const combinedApp = appHasBothPlatforms(plan.clients.map((client) => client.platform), discoverClientPlatforms(plan.projectRoot));
|
|
1159
|
+
/** The web dev origin this run settled on, for the environment's `webUrl`. */
|
|
1160
|
+
let webDevOrigin = null;
|
|
1161
|
+
/**
|
|
1162
|
+
* Whether that origin's `/oauth/callback` is known to be on the app's
|
|
1163
|
+
* magic-link allow-list already — because the app was created here with the
|
|
1164
|
+
* default-port seed, or because the dev-port step merged it (#2982). Anything
|
|
1165
|
+
* else has to be asked about before the environment points a sign-in link at
|
|
1166
|
+
* it.
|
|
1167
|
+
*/
|
|
1168
|
+
let devCallbackAllowListed = false;
|
|
1120
1169
|
for (const client of plan.clients) {
|
|
1121
1170
|
if (client.platform === "ios") {
|
|
1122
1171
|
// ── Apple (SwiftUI) path ────────────────────────────────────────
|
|
@@ -1129,13 +1178,13 @@ async function handleInit(projectDirArg, options) {
|
|
|
1129
1178
|
if (resolve(client.dir) !== resolve(plan.projectRoot)) {
|
|
1130
1179
|
const declared = declareSharedProjectConfig(client.dir, plan.projectRoot);
|
|
1131
1180
|
if (declared.length > 0) {
|
|
1132
|
-
success(`Xcode build phases read ${relative(client.dir, join(plan.projectRoot,
|
|
1181
|
+
success(`Xcode build phases read ${relative(client.dir, join(plan.projectRoot, PROJECT_TREE_DIR, PROJECT_CONFIG_FILENAME))}.`);
|
|
1133
1182
|
}
|
|
1134
1183
|
}
|
|
1135
1184
|
// Step 5: Generate primitive.json from the environment.
|
|
1136
1185
|
//
|
|
1137
1186
|
// In a current template the file is a build product, written by
|
|
1138
|
-
// `scripts/resolve-primitive-config.sh` from
|
|
1187
|
+
// `scripts/resolve-primitive-config.sh` from `primitive/config.json`
|
|
1139
1188
|
// (#2873) — it walks up to the project root, so a client inside a
|
|
1140
1189
|
// composed repository resolves the same one every other command does. A
|
|
1141
1190
|
// template that predates the change has no such script and a tracked
|
|
@@ -1150,7 +1199,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1150
1199
|
environmentName: adopted?.name,
|
|
1151
1200
|
});
|
|
1152
1201
|
if (result.outcome === "generated") {
|
|
1153
|
-
success("primitive.json generated from
|
|
1202
|
+
success("primitive.json generated from primitive/config.json.");
|
|
1154
1203
|
}
|
|
1155
1204
|
else if (result.outcome === "filled") {
|
|
1156
1205
|
success("primitive.json configured.");
|
|
@@ -1163,7 +1212,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1163
1212
|
// Neither the resolve script nor a template primitive.json — an
|
|
1164
1213
|
// unexpectedly old or trimmed template. The remedy is still never
|
|
1165
1214
|
// "type the app ID somewhere else": it is already in
|
|
1166
|
-
//
|
|
1215
|
+
// primitive/config.json, which is where every reader looks.
|
|
1167
1216
|
warn("This template has neither scripts/resolve-primitive-config.sh nor a primitive.json.");
|
|
1168
1217
|
warn(`The project config holds the app ID; update the template to generate primitive.json from it.`);
|
|
1169
1218
|
}
|
|
@@ -1219,7 +1268,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1219
1268
|
// Step 5: Fill in an OLD template's .env identity keys.
|
|
1220
1269
|
//
|
|
1221
1270
|
// A current template does not author them at all — `primitiveEnv()` reads
|
|
1222
|
-
//
|
|
1271
|
+
// `primitive/config.json`, which it finds by walking up — so "no keys to
|
|
1223
1272
|
// replace" is the expected outcome, not a problem (#2873). The fill-in
|
|
1224
1273
|
// survives only so a new CLI still produces a working scaffold from a
|
|
1225
1274
|
// template that predates the change.
|
|
@@ -1227,7 +1276,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1227
1276
|
try {
|
|
1228
1277
|
const envResult = updateEnvFile(client.dir, appId, serverUrl, appName, production);
|
|
1229
1278
|
if (!envResult.envUpdated) {
|
|
1230
|
-
info("No .env file in this template — identity comes from
|
|
1279
|
+
info("No .env file in this template — identity comes from primitive/config.json.");
|
|
1231
1280
|
}
|
|
1232
1281
|
else {
|
|
1233
1282
|
success("Environment configured.");
|
|
@@ -1243,7 +1292,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1243
1292
|
// ERR_USE_AFTER_CLOSE into the catch below (#2720).
|
|
1244
1293
|
try {
|
|
1245
1294
|
const defaultPort = DEFAULT_DEV_PORT;
|
|
1246
|
-
const defaultOrigin =
|
|
1295
|
+
const defaultOrigin = localDevOrigin(defaultPort);
|
|
1247
1296
|
const resolvedPort = await resolveDevPort({
|
|
1248
1297
|
configPort: initConfig?.dev_port,
|
|
1249
1298
|
hasConfig: initConfig != null,
|
|
@@ -1271,11 +1320,18 @@ async function handleInit(projectDirArg, options) {
|
|
|
1271
1320
|
},
|
|
1272
1321
|
});
|
|
1273
1322
|
const chosenPort = resolvedPort.port;
|
|
1323
|
+
// The same origin the app's CORS list and `emailRedirectUris` are seeded
|
|
1324
|
+
// with, so a combined app's first sign-in link is already allow-listed.
|
|
1325
|
+
webDevOrigin = localDevOrigin(chosenPort);
|
|
1326
|
+
// …but only for an app this run created: that seed happens at creation.
|
|
1327
|
+
// An adopted app on the default port has been through neither the seed
|
|
1328
|
+
// nor the port step below, so nothing here can vouch for its allow-list.
|
|
1329
|
+
devCallbackAllowListed = appWasCreatedByThisRun && chosenPort === defaultPort;
|
|
1274
1330
|
if (resolvedPort.source === "config") {
|
|
1275
1331
|
info(`Dev server port: ${chosenPort} (from config)`);
|
|
1276
1332
|
}
|
|
1277
1333
|
if (chosenPort !== defaultPort) {
|
|
1278
|
-
const devOrigin =
|
|
1334
|
+
const devOrigin = localDevOrigin(chosenPort);
|
|
1279
1335
|
// Pin the port in vite.config.ts and update .env. A vite config whose
|
|
1280
1336
|
// shape we can't patch must be said out loud — otherwise the dev
|
|
1281
1337
|
// server keeps 5173 while .env and the redirect URIs point elsewhere.
|
|
@@ -1351,6 +1407,9 @@ async function handleInit(projectDirArg, options) {
|
|
|
1351
1407
|
};
|
|
1352
1408
|
}
|
|
1353
1409
|
await apiClient.updateAppSettings(appId, settingsUpdate);
|
|
1410
|
+
// This merge is what puts the chosen port's callback on the
|
|
1411
|
+
// allow-list, whoever created the app (#2982).
|
|
1412
|
+
devCallbackAllowListed = true;
|
|
1354
1413
|
success(`Dev port set to ${chosenPort}: ${viteUpdated ? "vite.config.ts, " : ""}.env, CORS, redirect URIs, and base URL updated.`);
|
|
1355
1414
|
}
|
|
1356
1415
|
catch (err) {
|
|
@@ -1448,6 +1507,32 @@ async function handleInit(projectDirArg, options) {
|
|
|
1448
1507
|
}
|
|
1449
1508
|
}
|
|
1450
1509
|
}
|
|
1510
|
+
// The combined app's dev sign-in link (#2982): the environment learns the
|
|
1511
|
+
// web origin this run settled on, so the iOS client's emailed link opens the
|
|
1512
|
+
// local web app instead of a scheme that is dead in the Simulator. Only when
|
|
1513
|
+
// this run owns the project config — an adopting run gets the guidance and
|
|
1514
|
+
// decides for itself.
|
|
1515
|
+
//
|
|
1516
|
+
// And only once the app actually allows that callback. Setting `webUrl` is
|
|
1517
|
+
// what turns a code-only email into a link, and a link the server refuses is
|
|
1518
|
+
// a 400 on every sign-in request — strictly worse than the code-only default
|
|
1519
|
+
// this app has today. An app created here was seeded with the callback; an
|
|
1520
|
+
// adopted one is asked about, and when the answer is no or unavailable the
|
|
1521
|
+
// seed is skipped in favour of printing the step.
|
|
1522
|
+
let seededWebOrigin = null;
|
|
1523
|
+
let unverifiedDevCallback = null;
|
|
1524
|
+
if (combinedApp && plan.writesProjectConfig && webDevOrigin) {
|
|
1525
|
+
const devCallback = `${webDevOrigin}/oauth/callback`;
|
|
1526
|
+
const allowed = devCallbackAllowListed ||
|
|
1527
|
+
(await devCallbackIsAllowListed(appId, devCallback, credentials));
|
|
1528
|
+
if (allowed && seedEnvironmentWebUrl(plan.projectRoot, "dev", webDevOrigin)) {
|
|
1529
|
+
seededWebOrigin = webDevOrigin;
|
|
1530
|
+
success(`Environment "dev" points at the web client: webUrl ${webDevOrigin}`);
|
|
1531
|
+
}
|
|
1532
|
+
else if (!allowed) {
|
|
1533
|
+
unverifiedDevCallback = devCallback;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1451
1536
|
// Step 10: Offer to install Claude Code skill if not already installed
|
|
1452
1537
|
try {
|
|
1453
1538
|
const { isSkillInstalled, installSkillExplicit, checkSkillStatus } = await import("../lib/skill-installer.js");
|
|
@@ -1505,12 +1590,25 @@ async function handleInit(projectDirArg, options) {
|
|
|
1505
1590
|
}
|
|
1506
1591
|
divider();
|
|
1507
1592
|
// Email sign-in already works for the scaffolded app — code-only, no app
|
|
1508
|
-
// settings — so this is an opt-in, not a step (#2969).
|
|
1509
|
-
|
|
1510
|
-
|
|
1593
|
+
// settings — so this is an opt-in, not a step (#2969). An app with both
|
|
1594
|
+
// clients gets the https link instead, which needs a different checklist
|
|
1595
|
+
// (#2982).
|
|
1596
|
+
if (combinedApp) {
|
|
1597
|
+
for (const line of universalLinkGuidance({
|
|
1598
|
+
webOrigin: seededWebOrigin ?? undefined,
|
|
1599
|
+
unverifiedDevCallback: unverifiedDevCallback ?? undefined,
|
|
1600
|
+
})) {
|
|
1511
1601
|
info(line);
|
|
1602
|
+
}
|
|
1512
1603
|
divider();
|
|
1513
1604
|
}
|
|
1605
|
+
else {
|
|
1606
|
+
for (const { scheme, registered } of iosCallbackSchemes) {
|
|
1607
|
+
for (const line of linkOptInGuidance(scheme, { registered }))
|
|
1608
|
+
info(line);
|
|
1609
|
+
divider();
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1514
1612
|
if (plan.mode === "add-client") {
|
|
1515
1613
|
info(`No .primitive/ and no .git/ were created inside the new client — this app has one of each, at ${plan.projectRoot}.`);
|
|
1516
1614
|
info(`The new files are uncommitted: review them with \`git status\` in ${plan.projectRoot} and commit when you are happy.`);
|
|
@@ -1550,7 +1648,7 @@ async function handleInit(projectDirArg, options) {
|
|
|
1550
1648
|
}
|
|
1551
1649
|
/** Where a project root's environments live, for the production derivation. */
|
|
1552
1650
|
function readProjectEnvironments(projectRoot) {
|
|
1553
|
-
const configPath = join(projectRoot,
|
|
1651
|
+
const configPath = join(projectRoot, PROJECT_TREE_DIR, PROJECT_CONFIG_FILENAME);
|
|
1554
1652
|
if (!existsSync(configPath))
|
|
1555
1653
|
return undefined;
|
|
1556
1654
|
try {
|
|
@@ -1581,22 +1679,12 @@ function discoverExistingClients(projectRoot) {
|
|
|
1581
1679
|
for (const entry of entries) {
|
|
1582
1680
|
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
1583
1681
|
continue;
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
return false;
|
|
1591
|
-
}
|
|
1592
|
-
})();
|
|
1593
|
-
if (existsSync(join(dir, "Package.swift")) || hasXcodeProject) {
|
|
1594
|
-
found.push({ platform: "ios", dir: entry.name });
|
|
1595
|
-
}
|
|
1596
|
-
else if (existsSync(join(dir, "package.json")) &&
|
|
1597
|
-
existsSync(join(dir, "vite.config.ts"))) {
|
|
1598
|
-
found.push({ platform: "web", dir: entry.name });
|
|
1599
|
-
}
|
|
1682
|
+
// The same template signals the both-platforms predicate reads, so the
|
|
1683
|
+
// layout description and the universal-link wiring cannot disagree about
|
|
1684
|
+
// what counts as a client.
|
|
1685
|
+
const platform = detectClientPlatform(join(projectRoot, entry.name));
|
|
1686
|
+
if (platform)
|
|
1687
|
+
found.push({ platform, dir: entry.name });
|
|
1600
1688
|
}
|
|
1601
1689
|
return found;
|
|
1602
1690
|
}
|