@topolo/cli 0.6.0 → 0.6.2

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.
@@ -845,7 +845,12 @@ function emitCloudcontrol() {
845
845
  }
846
846
  }
847
847
  cc.tags = existing.tags || ['application', 'cloudflare', 'github-actions', 'javascript'];
848
- cc.cloudflare_account_id = manifest.environments.production.accountId;
848
+ const primaryEnvironment = [...ENVS]
849
+ .reverse()
850
+ .find((environment) => deployableEnvironments.has(environment));
851
+ if (primaryEnvironment) {
852
+ cc.cloudflare_account_id = manifest.environments[primaryEnvironment].accountId;
853
+ }
849
854
 
850
855
  return JSON.stringify(cc, null, 2) + '\n';
851
856
  }
package/dist/index.js CHANGED
@@ -4724,14 +4724,26 @@ function isEvidenceStampFresh(stamp) {
4724
4724
  return Date.now() - recordedAt <= EVIDENCE_FRESHNESS_MS;
4725
4725
  }
4726
4726
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([
4727
+ ".action-normalize",
4728
+ ".claude",
4729
+ ".codex-worktrees",
4730
+ ".deploy",
4727
4731
  ".git",
4728
4732
  ".next",
4733
+ ".playwright",
4734
+ ".topolo-tmp",
4729
4735
  ".turbo",
4736
+ ".worktrees",
4730
4737
  ".wrangler",
4738
+ "_tmp",
4739
+ "_worktrees",
4740
+ "artifacts",
4731
4741
  "build",
4732
4742
  "coverage",
4733
4743
  "dist",
4734
- "node_modules"
4744
+ "node_modules",
4745
+ "output",
4746
+ "tmp"
4735
4747
  ]);
4736
4748
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
4737
4749
  ".cjs",
@@ -5416,8 +5428,11 @@ function sharedAuthEvidence(evidence) {
5416
5428
  if (text.includes("TopoloAuthMiddleware")) items.push("source=TopoloAuthMiddleware");
5417
5429
  if (text.includes("TopoloClient")) items.push("source=TopoloClient");
5418
5430
  if (text.includes("TopoloOAuth")) items.push("source=TopoloOAuth");
5419
- if (/\.initialize(?:Session)?\(\s*\)/.test(text)) items.push("auth-boot=blocking-cookie-refresh");
5420
- if (/cookieRefresh:\s*['"]background['"]/.test(text)) items.push("auth-boot=background-cookie-refresh");
5431
+ const browserRecords = productionBrowserSourceRecords(evidence);
5432
+ const blockingBoot = browserRecords.find(({ text: source }) => /\b(?:auth|authClient|topoloAuth)\.initialize(?:Session)?\(\s*\)/i.test(source));
5433
+ const backgroundBoot = browserRecords.find(({ text: source }) => /cookieRefresh:\s*['"]background['"]/.test(source));
5434
+ if (blockingBoot) items.push("auth-boot=blocking-cookie-refresh", `auth-boot-file=${blockingBoot.path}`);
5435
+ if (backgroundBoot) items.push("auth-boot=background-cookie-refresh", `auth-background-file=${backgroundBoot.path}`);
5421
5436
  if (/Authorization.*Bearer/i.test(text) && (text.includes("/validate") || text.includes("TOPOLO_AUTH_URL") || text.includes("AUTH_BASE_URL") || text.includes("auth.topolo.app"))) {
5422
5437
  items.push("source=auth-validate-fetch");
5423
5438
  }
@@ -5436,7 +5451,8 @@ function documentedOperatorLocalAuthBoundaryEvidence(evidence) {
5436
5451
  }
5437
5452
  function sharedShellEvidence(evidence) {
5438
5453
  const deps = dependencyNames(evidence);
5439
- const text = evidence.source.text;
5454
+ const browserRecords = productionBrowserSourceRecords(evidence);
5455
+ const text = browserRecords.map((record4) => record4.text).join("\n");
5440
5456
  const items = [];
5441
5457
  if (deps.has("@topolo-io/app-shell")) items.push("dependency=@topolo-io/app-shell");
5442
5458
  if (deps.has("@topolo-io/ui-kit")) items.push("dependency=@topolo-io/ui-kit");
@@ -5456,11 +5472,9 @@ function sharedShellEvidence(evidence) {
5456
5472
  const hasVisualQaFixtures = hasVisualQaMode && /fixture|visualQaResponse|visualQaApi|mock[A-Z]\w*|devpreview/i.test(text) && /fetch|useQuery|api/i.test(text);
5457
5473
  if (hasVisualQaFixtures) items.push("source=visual-qa-fixtures");
5458
5474
  if (/from\s+['"]lucide-react['"]/.test(text)) items.push("local=direct-lucide-react");
5459
- if (/(?:icon|Icon)\s*:\s*['"][◈◇⬡⬢▣◉✉▷✦○◆✕⚙🔑💡⚠✓♫]/.test(text)) {
5460
- items.push("local=raw-symbol-icons");
5461
- }
5462
- if (/(?:<span[^>]*>|<div[^>]*>)[^<]{0,16}[◈◇⬡⬢▣◉✉▷✦○◆✕⚙🔑💡⚠✓♫]/.test(text)) {
5463
- items.push("local=raw-symbol-icons");
5475
+ const rawSymbolRecord = browserRecords.find(({ text: source }) => /(?:icon|Icon)\s*:\s*['"][◈◇⬡⬢▣◉✉▷✦○◆✕⚙🔑💡⚠✓♫]/.test(source) || /(?:<span[^>]*>|<div[^>]*>)[^<]{0,16}[◈◇⬡⬢▣◉✉▷✦○◆✕⚙🔑💡⚠✓♫]/.test(source));
5476
+ if (rawSymbolRecord) {
5477
+ items.push("local=raw-symbol-icons", `raw-symbol-file=${rawSymbolRecord.path}`);
5464
5478
  }
5465
5479
  if (text.includes(["Topolo", "Workspace", "Shell"].join(""))) items.push("forbidden=workspace-shell");
5466
5480
  if (/\bTopoloShell\b/.test(text)) items.push("legacy=TopoloShellFrame");
@@ -5490,6 +5504,16 @@ function hasLocalBrowserSurface(evidence) {
5490
5504
  (file) => file.endsWith(".tsx") || file.endsWith(".jsx") || file.includes("/src/client/") || file.includes("/client/")
5491
5505
  );
5492
5506
  }
5507
+ function productionBrowserSourceRecords(evidence) {
5508
+ return evidence.source.records.filter((record4) => {
5509
+ const sourcePath = record4.path.toLowerCase();
5510
+ if (/(^|\/)(?:__tests__|test|tests|scripts|fixtures|migrations)(\/|$)/.test(sourcePath)) return false;
5511
+ if (/\.(?:test|spec)\.[^.]+$/.test(sourcePath)) return false;
5512
+ if (/\/apps\/(?:api|analytics|gateway|jobs|queue|runtime|worker)\//.test(sourcePath)) return false;
5513
+ if (/\.(?:tsx|jsx)$/.test(sourcePath)) return true;
5514
+ return /\/(?:web|marketing|desktop)\/src\//.test(sourcePath) && /\.(?:ts|js)$/.test(sourcePath);
5515
+ });
5516
+ }
5493
5517
  function organizationScopeEvidence(evidence) {
5494
5518
  const text = evidence.source.text;
5495
5519
  const items = [];
@@ -5657,18 +5681,21 @@ function findWranglerFiles(appRoot) {
5657
5681
  return ["wrangler.json", "wrangler.jsonc", "wrangler.toml"].filter((file) => isFile(path.join(appRoot, file))).map((file) => relativeTopoloPath(path.join(appRoot, file)));
5658
5682
  }
5659
5683
  function readSourceIndex(root) {
5660
- if (!isDirectory(root)) return { files: [], text: "" };
5684
+ if (!isDirectory(root)) return { files: [], text: "", records: [] };
5661
5685
  const files = collectSourceFiles(root, 4e3);
5662
- const chunks = files.map((file) => {
5686
+ const records = files.map((file) => {
5687
+ let text = "";
5663
5688
  try {
5664
- return readFileSync3(file, "utf8");
5689
+ text = readFileSync3(file, "utf8");
5665
5690
  } catch {
5666
- return "";
5691
+ text = "";
5667
5692
  }
5693
+ return { path: relativeTopoloPath(file), text };
5668
5694
  });
5669
5695
  return {
5670
- files: files.map(relativeTopoloPath),
5671
- text: chunks.join("\n")
5696
+ files: records.map((record4) => record4.path),
5697
+ text: records.map((record4) => record4.text).join("\n"),
5698
+ records
5672
5699
  };
5673
5700
  }
5674
5701
  function collectSourceFiles(root, limit) {
@@ -6192,7 +6219,7 @@ var APPLICATION_CAPABILITY_REGISTRY = {
6192
6219
  platform: ["@topolo-io/notifications"],
6193
6220
  developer: []
6194
6221
  }, [], [], [], ["notification-consumer"], ["notification-inbox-smoke"]),
6195
- notification_producer: capability("notification_producer", "TopoloNotify + TopoloDevelopers", platformTarget, ["notifications", "api"], ["api"], {
6222
+ notification_producer: capability("notification_producer", "TopoloNotify + TopoloDevelopers", platformTarget, ["api"], ["api"], {
6196
6223
  platform: ["@topolo-io/notifications", "@topolo-io/auth-middleware"],
6197
6224
  developer: []
6198
6225
  }, [], ["events:write"], [], ["notification-producer-adapter", "notification-contracts"], ["notification-delivery-smoke"]),
@@ -9396,20 +9423,13 @@ function scaffoldFiles(options) {
9396
9423
  `CREATE TABLE IF NOT EXISTS ${tablePrefix}_workspaces (`,
9397
9424
  " id TEXT PRIMARY KEY,",
9398
9425
  ` ${tenantColumn} TEXT NOT NULL,`,
9399
- " name TEXT NOT NULL,",
9400
- " slug TEXT NOT NULL,",
9401
- " created_by_user_id TEXT,",
9402
- " created_by_email TEXT,",
9426
+ " status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')),",
9403
9427
  " created_at TEXT NOT NULL,",
9404
- " updated_at TEXT NOT NULL,",
9405
- ` UNIQUE(${tenantColumn}, slug)`,
9428
+ " updated_at TEXT NOT NULL",
9406
9429
  ");",
9407
9430
  "",
9408
9431
  `CREATE INDEX IF NOT EXISTS idx_${tablePrefix}_workspaces_${tenantColumn}`,
9409
9432
  ` ON ${tablePrefix}_workspaces(${tenantColumn});`,
9410
- "",
9411
- `CREATE INDEX IF NOT EXISTS idx_${tablePrefix}_workspaces_slug`,
9412
- ` ON ${tablePrefix}_workspaces(${tenantColumn}, slug);`,
9413
9433
  ""
9414
9434
  ].join("\n");
9415
9435
  const base = target === "platform" ? firstPartyAppPath(application.appDir, options.payload.audience) : application.appDir;
@@ -9972,10 +9992,10 @@ export default defineConfig({
9972
9992
  if (options.payload.resources.d1) {
9973
9993
  files.push({
9974
9994
  path: `${apiBase}/migrations/0001_initial.sql`,
9975
- purpose: "Canonical multi-tenant baseline: the app workspaces table.",
9995
+ purpose: "Canonical multi-tenant baseline: the identity-only workspace mirror (platform owns the record).",
9976
9996
  requiredSignals: [
9977
- `${tablePrefix}_workspaces carries ${tenantColumn} on every row`,
9978
- "every domain table added later carries the tenant + workspace columns"
9997
+ `${tablePrefix}_workspaces is an identity-only mirror (id, ${tenantColumn}, status) \u2014 no name/slug/is_default; the workspace record lives in TopoloAuth`,
9998
+ `every domain table added later carries ${tenantColumn} and a workspace_id scope column`
9979
9999
  ],
9980
10000
  content: workspacesMigrationSql
9981
10001
  });
@@ -10267,6 +10287,22 @@ async function reconcileApplicationCapabilitySource(options) {
10267
10287
  distributionModel: registration.distributionModel === "marketplace" ? "marketplace" : "organization_internal",
10268
10288
  requested: [...existing, ...options.additions ?? []]
10269
10289
  });
10290
+ const marketplace = record(registration.marketplace);
10291
+ const unlistedNativeArtifact = options.ownerType === "topolo_first_party" && registration.surfaceModel === "mobile_container" && registration.distributionModel === "organization_internal" && marketplace?.intent === "unlisted";
10292
+ if (unlistedNativeArtifact) {
10293
+ return {
10294
+ mode: options.mode,
10295
+ applied: false,
10296
+ status: "in_sync",
10297
+ manifestPath,
10298
+ packagePath,
10299
+ contractPath,
10300
+ capabilities,
10301
+ changes: [],
10302
+ missingResources: [],
10303
+ verticalConfiguration: []
10304
+ };
10305
+ }
10270
10306
  const expectedComposition = capabilityCompositionProjection(capabilities);
10271
10307
  const expectedNotifications = capabilities.selected.includes("notification_producer") ? { role: "producer", contracts: "topolo.notification-contracts.json" } : capabilities.selected.includes("notifications") ? { role: "consumer_only" } : { role: "internal_non_producer" };
10272
10308
  const changes = [];
@@ -10421,7 +10457,10 @@ function manifestProvidesResource(manifest, resource) {
10421
10457
  return false;
10422
10458
  }
10423
10459
  function packageVersion(packageName, target) {
10424
- if (target === "platform") return "latest";
10460
+ if (target === "platform") {
10461
+ if (packageName === "@topolo-io/notifications") return "^0.6.8";
10462
+ return "latest";
10463
+ }
10425
10464
  if (packageName === "@topolo/login") return "^0.1.0";
10426
10465
  if (packageName === "@topolo/sdk") return "^0.8.1";
10427
10466
  throw new Error(`Capability registry exposed private package ${packageName} to a third-party application.`);
@@ -10530,10 +10569,19 @@ function resolveManifestApplicationUrl(manifest, environment) {
10530
10569
  if (hostOverride) return `https://${hostOverride.replace(/^https?:\/\//, "").replace(/\/+$/, "")}`;
10531
10570
  const domain = manifest.environments?.[environment]?.domain;
10532
10571
  if (!target.subdomain || !domain) {
10572
+ if (manifest.registration?.productionUrl) {
10573
+ return manifest.registration.productionUrl.replace(/\/+$/, "");
10574
+ }
10533
10575
  throw new Error(`Cannot derive the ${environment} application URL from target ${targetName}.`);
10534
10576
  }
10535
10577
  return `https://${target.subdomain}.${domain}`;
10536
10578
  }
10579
+ function resolveManifestHealthcheckPath(manifest) {
10580
+ const targets = manifest.targets || {};
10581
+ const targetName = manifest.primaryTarget || (targets.web ? "web" : Object.keys(targets)[0]);
10582
+ const healthcheckPath = targetName ? targets[targetName]?.healthcheckPath?.trim() : "";
10583
+ return healthcheckPath && healthcheckPath.startsWith("/") && !healthcheckPath.startsWith("//") ? healthcheckPath : "/health";
10584
+ }
10537
10585
  function assertRegistration(value) {
10538
10586
  const registration = record2(value);
10539
10587
  const required = [
@@ -10549,10 +10597,11 @@ function assertRegistration(value) {
10549
10597
  "version",
10550
10598
  "pricing",
10551
10599
  "supportEmail",
10552
- "auth",
10553
10600
  "capabilities",
10554
10601
  "marketplace"
10555
10602
  ];
10603
+ const marketplace = record2(registration?.marketplace);
10604
+ if (marketplace?.intent !== "unlisted") required.push("auth");
10556
10605
  const missing = required.filter((field) => registration?.[field] == null || registration[field] === "");
10557
10606
  if (missing.length) {
10558
10607
  throw new Error(
@@ -10656,7 +10705,7 @@ async function loadApplicationReconcileRequest(options) {
10656
10705
  }
10657
10706
  const environment = options.environment || selectedEnvironment();
10658
10707
  const productionUrl = resolveManifestApplicationUrl(manifest, environment);
10659
- const callbackPath = registration.auth.callbackPath.startsWith("/") ? registration.auth.callbackPath : `/${registration.auth.callbackPath}`;
10708
+ const callbackPath = registration.auth ? registration.auth.callbackPath.startsWith("/") ? registration.auth.callbackPath : `/${registration.auth.callbackPath}` : null;
10660
10709
  const appId = String(manifest.appId || manifest.app_id || "").trim();
10661
10710
  if (!appId) throw new Error("topolo.app.json must declare the immutable appId.");
10662
10711
  return {
@@ -10680,13 +10729,14 @@ async function loadApplicationReconcileRequest(options) {
10680
10729
  pricingType: registration.pricing.type,
10681
10730
  monthlyPrice: registration.pricing.monthlyPrice ?? null,
10682
10731
  productionUrl,
10732
+ healthcheckPath: resolveManifestHealthcheckPath(manifest),
10683
10733
  demoUrl: registration.ownerType === "topolo_first_party" ? productionUrl : registration.demoUrl ?? productionUrl,
10684
10734
  repoUrl: registration.repoUrl ?? null,
10685
10735
  supportEmail: registration.supportEmail,
10686
10736
  tags: registration.tags ?? [],
10687
10737
  screenshots: registration.screenshots ?? [],
10688
- callbackUrls: [`${productionUrl}${callbackPath}`],
10689
- requestedScopes: registration.auth.requestedScopes,
10738
+ callbackUrls: callbackPath ? [`${productionUrl}${callbackPath}`] : [],
10739
+ requestedScopes: registration.auth?.requestedScopes ?? [],
10690
10740
  capabilities: registration.capabilities,
10691
10741
  marketplace: { ...registration.marketplace, appUrl: productionUrl },
10692
10742
  notifications: manifest.notifications ?? null
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
- "version": "0.6.0",
4
- "topoloPublishedVersion": "0.6.0",
3
+ "version": "0.6.2",
4
+ "topoloPublishedVersion": "0.6.2",
5
5
  "description": "Command-line interface for Topolo developers, operators, and agents, with internal first-party scaffolding available under the topolo platform namespace.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -31,10 +31,10 @@
31
31
  "devDependencies": {
32
32
  "@topolo/sdk": "latest",
33
33
  "@types/node": "^20.12.0",
34
- "@vitest/coverage-v8": "^2.1.0",
34
+ "@vitest/coverage-v8": "3.2.6",
35
35
  "tsup": "^8.0.0",
36
36
  "typescript": "^5.4.0",
37
- "vitest": "^2.1.0"
37
+ "vitest": "3.2.6"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=20"