@treeseed/sdk 0.6.7 → 0.6.8

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.
Files changed (48) hide show
  1. package/dist/copilot.d.ts +15 -0
  2. package/dist/copilot.js +75 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +18 -0
  5. package/dist/managed-dependencies.d.ts +56 -0
  6. package/dist/managed-dependencies.js +668 -0
  7. package/dist/operations/providers/default.js +30 -1
  8. package/dist/operations/services/commit-message-provider.d.ts +33 -0
  9. package/dist/operations/services/commit-message-provider.js +319 -0
  10. package/dist/operations/services/config-runtime.js +41 -20
  11. package/dist/operations/services/git-remote-policy.d.ts +9 -0
  12. package/dist/operations/services/git-remote-policy.js +55 -0
  13. package/dist/operations/services/git-workflow.js +22 -3
  14. package/dist/operations/services/github-api.js +9 -4
  15. package/dist/operations/services/knowledge-coop-launch.js +4 -0
  16. package/dist/operations/services/local-dev.js +7 -2
  17. package/dist/operations/services/package-reference-policy.d.ts +70 -0
  18. package/dist/operations/services/package-reference-policy.js +314 -0
  19. package/dist/operations/services/project-platform.d.ts +4 -0
  20. package/dist/operations/services/project-platform.js +28 -4
  21. package/dist/operations/services/railway-deploy.d.ts +4 -1
  22. package/dist/operations/services/railway-deploy.js +76 -38
  23. package/dist/operations/services/repository-save-orchestrator.d.ts +172 -0
  24. package/dist/operations/services/repository-save-orchestrator.js +1462 -0
  25. package/dist/operations/services/workspace-dependency-mode.d.ts +70 -0
  26. package/dist/operations/services/workspace-dependency-mode.js +404 -0
  27. package/dist/operations/services/workspace-preflight.js +5 -0
  28. package/dist/operations/services/workspace-save.js +10 -6
  29. package/dist/operations-registry.js +5 -0
  30. package/dist/operations-types.d.ts +1 -0
  31. package/dist/platform/books-data.js +4 -1
  32. package/dist/platform/env.yaml +6 -3
  33. package/dist/reconcile/builtin-adapters.js +37 -7
  34. package/dist/scripts/cleanup-markdown.js +4 -0
  35. package/dist/scripts/publish-package.js +5 -0
  36. package/dist/scripts/tenant-workflow-action.js +11 -2
  37. package/dist/verification.js +24 -12
  38. package/dist/workflow/operations.d.ts +381 -55
  39. package/dist/workflow/operations.js +718 -258
  40. package/dist/workflow-state.d.ts +40 -1
  41. package/dist/workflow-state.js +220 -17
  42. package/dist/workflow-support.d.ts +3 -0
  43. package/dist/workflow-support.js +34 -0
  44. package/dist/workflow.d.ts +19 -3
  45. package/dist/workflow.js +3 -3
  46. package/dist/wrangler-d1.js +6 -1
  47. package/package.json +17 -1
  48. package/templates/github/deploy.workflow.yml +24 -14
@@ -325,6 +325,31 @@ function ensureCloudflareDnsRecord(env, zoneId, record) {
325
325
  }
326
326
  })?.result ?? null;
327
327
  }
328
+ function unquoteDnsTxtContent(value) {
329
+ const trimmed = value.trim();
330
+ return trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"') ? trimmed.slice(1, -1) : trimmed;
331
+ }
332
+ function dnsRecordContentMatches(actual, expected, type) {
333
+ const actualText = String(actual ?? "");
334
+ const expectedText = String(expected ?? "");
335
+ if (String(type ?? "").toUpperCase() === "TXT") {
336
+ return unquoteDnsTxtContent(actualText) === unquoteDnsTxtContent(expectedText);
337
+ }
338
+ return actualText === expectedText;
339
+ }
340
+ function dnsRecordProxiedMatches(actual, expected) {
341
+ return expected.proxied === void 0 || Boolean(actual?.proxied) === Boolean(expected.proxied);
342
+ }
343
+ function dnsRecordIdentityMatches(actual, expected) {
344
+ return actual?.name === expected.name && String(actual?.type ?? "").toUpperCase() === expected.type;
345
+ }
346
+ function dnsRecordMatches(actual, expected) {
347
+ return Boolean(actual) && dnsRecordIdentityMatches(actual, expected) && dnsRecordContentMatches(actual?.content, expected.content, expected.type) && dnsRecordProxiedMatches(actual, expected);
348
+ }
349
+ function dnsRecordsFromCurrentResult(input) {
350
+ const records = input.result?.state?.records;
351
+ return Array.isArray(records) ? records.filter((entry) => Boolean(entry) && typeof entry === "object") : [];
352
+ }
328
353
  function getCloudflarePagesDomain(env, projectName, domain) {
329
354
  const accountId = env.CLOUDFLARE_ACCOUNT_ID;
330
355
  if (!accountId) {
@@ -1600,7 +1625,7 @@ function observeDnsRecordUnit(input) {
1600
1625
  (record) => listCloudflareDnsRecords(env, zoneId, record.name).find((entry) => entry?.name === record.name && entry?.type === record.type) ?? null
1601
1626
  ) : [];
1602
1627
  const matches = desiredRecords.length > 0 && liveRecords.every(
1603
- (entry, index) => Boolean(entry) && entry?.content === desiredRecords[index]?.content && (desiredRecords[index]?.proxied === void 0 || Boolean(entry?.proxied) === Boolean(desiredRecords[index]?.proxied))
1628
+ (entry, index) => dnsRecordMatches(entry, desiredRecords[index])
1604
1629
  );
1605
1630
  return {
1606
1631
  exists: desiredRecords.length > 0 && liveRecords.every(Boolean),
@@ -1673,14 +1698,19 @@ function verifyDnsRecordUnit(input) {
1673
1698
  ]);
1674
1699
  }
1675
1700
  const checks = desiredRecords.map((record, index) => {
1676
- const live = listCloudflareDnsRecords(env, zoneId, record.name).find((entry) => entry?.name === record.name && entry?.type === record.type) ?? null;
1677
- const proxiedMatches = record.proxied === void 0 ? true : Boolean(live?.proxied) === Boolean(record.proxied);
1701
+ const live = listCloudflareDnsRecords(env, zoneId, record.name).find((entry) => dnsRecordIdentityMatches(entry, record)) ?? null;
1702
+ const reconciled = dnsRecordsFromCurrentResult(input).find(
1703
+ (entry) => dnsRecordIdentityMatches(entry, record)
1704
+ ) ?? null;
1705
+ const effective = dnsRecordMatches(live, record) ? live : reconciled ?? live;
1706
+ const contentMatches = dnsRecordContentMatches(effective?.content, record.content, record.type);
1707
+ const proxiedMatches = dnsRecordProxiedMatches(effective, record);
1678
1708
  return verificationCheck(`dns-record:${index + 1}`, `DNS record ${record.type} ${record.name} matches the desired value`, "api", {
1679
- exists: Boolean(live?.id),
1680
- configured: live?.content === record.content && proxiedMatches,
1709
+ exists: Boolean(effective?.id),
1710
+ configured: contentMatches && proxiedMatches,
1681
1711
  expected: `${record.type} ${record.name} -> ${record.content}${record.proxied === void 0 ? "" : ` proxied=${record.proxied}`}`,
1682
- observed: live ? `${live.type} ${live.name} -> ${live.content}${typeof live.proxied === "boolean" ? ` proxied=${live.proxied}` : ""}` : null,
1683
- issues: live?.id ? live.content === record.content && proxiedMatches ? [] : [`DNS record ${record.name} does not match the expected value.`] : [`DNS record ${record.type} ${record.name} is missing.`]
1712
+ observed: effective ? `${effective.type} ${effective.name} -> ${effective.content}${typeof effective.proxied === "boolean" ? ` proxied=${effective.proxied}` : ""}` : null,
1713
+ issues: effective?.id ? contentMatches && proxiedMatches ? [] : [`DNS record ${record.name} does not match the expected value.`] : [`DNS record ${record.type} ${record.name} is missing.`]
1684
1714
  });
1685
1715
  });
1686
1716
  return summarizeVerification(input.unit.unitId, checks);
@@ -336,6 +336,10 @@ async function runCli(argv = process.argv.slice(2)) {
336
336
  const mode = args.check ? 'check' : 'write';
337
337
  const targets = await collectMarkdownTargets(args.targets);
338
338
  if (targets.length === 0) {
339
+ if (mode === 'check') {
340
+ console.log('No Markdown files found for cleanup.');
341
+ return 0;
342
+ }
339
343
  console.error('No Markdown files found for cleanup.');
340
344
  return 1;
341
345
  }
@@ -3,6 +3,11 @@ import { resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  const packageRoot = resolve(fileURLToPath(new URL('..', import.meta.url)));
5
5
  const extraArgs = process.argv.slice(2);
6
+ const tagName = process.env.GITHUB_REF_NAME;
7
+ if (tagName && !/^\d+\.\d+\.\d+$/.test(tagName)) {
8
+ console.error(`Refusing to publish @treeseed/sdk from non-stable tag "${tagName}".`);
9
+ process.exit(1);
10
+ }
6
11
  const npmArgs = ['publish', '.', '--access', 'public'];
7
12
  if (process.env.GITHUB_ACTIONS === 'true') {
8
13
  npmArgs.push('--provenance');
@@ -75,7 +75,16 @@ async function main() {
75
75
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
76
76
  }
77
77
  }
78
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
78
+ function isCliEntrypoint() {
79
+ if (!process.argv[1]) {
80
+ return false;
81
+ }
82
+ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
83
+ return true;
84
+ }
85
+ return /(?:^|\/)tenant-workflow-action\.(?:ts|js)$/u.test(process.argv[1]);
86
+ }
87
+ if (isCliEntrypoint()) {
79
88
  await main();
80
89
  }
81
- export { parseArgs };
90
+ export { isCliEntrypoint, parseArgs };
@@ -3,6 +3,8 @@ import * as childProcess from "node:child_process";
3
3
  import { basename, relative, resolve } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { createTreeseedManagedToolEnv, resolveTreeseedToolBinary } from "./managed-dependencies.js";
7
+ const defaultActUbuntuLatestImage = "catthehacker/ubuntu:act-latest";
6
8
  function defaultWrite(message, stream = "stdout") {
7
9
  if (!message) return;
8
10
  (stream === "stderr" ? process.stderr : process.stdout).write(`${message}
@@ -11,7 +13,7 @@ function defaultWrite(message, stream = "stdout") {
11
13
  function run(command, args, cwd) {
12
14
  const result = childProcess.spawnSync(command, args, {
13
15
  cwd,
14
- env: process.env,
16
+ env: createTreeseedManagedToolEnv(process.env),
15
17
  stdio: "inherit"
16
18
  });
17
19
  return result.status ?? 1;
@@ -19,7 +21,7 @@ function run(command, args, cwd) {
19
21
  function check(command, args, cwd) {
20
22
  const result = childProcess.spawnSync(command, args, {
21
23
  cwd,
22
- env: process.env,
24
+ env: createTreeseedManagedToolEnv(process.env),
23
25
  stdio: "pipe",
24
26
  encoding: "utf8"
25
27
  });
@@ -39,6 +41,14 @@ function readPackageManifest(packageJsonPath) {
39
41
  return null;
40
42
  }
41
43
  }
44
+ function createActArgs(eventName, workflowPath) {
45
+ const image = process.env.TREESEED_VERIFY_ACT_UBUNTU_LATEST_IMAGE?.trim() || defaultActUbuntuLatestImage;
46
+ const args = ["act", eventName, "-W", workflowPath, "-j", "verify"];
47
+ if (image) {
48
+ args.push("-P", `ubuntu-latest=${image}`);
49
+ }
50
+ return args;
51
+ }
42
52
  function createWorkspaceActWorkflow(options) {
43
53
  const relativePackageRoot = relative(options.workspaceRoot, options.packageRoot).replace(/\\/g, "/");
44
54
  const siblingPreparationCommands = options.localTreeseedSiblingDependencies.map((packageName) => {
@@ -49,9 +59,9 @@ function createWorkspaceActWorkflow(options) {
49
59
  }
50
60
  const commands = [
51
61
  `if test -f ${packageDir}/package-lock.json; then`,
52
- ` npm --prefix ${packageDir} ci`,
62
+ ` npm --prefix ${packageDir} ci --workspaces=false`,
53
63
  "else",
54
- ` npm --prefix ${packageDir} install --no-audit --no-fund`,
64
+ ` npm --prefix ${packageDir} install --workspaces=false --no-audit --no-fund`,
55
65
  "fi"
56
66
  ];
57
67
  if (manifest.scripts?.["build:dist"]) {
@@ -102,9 +112,9 @@ ${siblingPreparationCommands.split("\n").map((line) => ` ${line}`).join
102
112
  ` : ""} - name: Install dependencies
103
113
  run: |
104
114
  if test -f package-lock.json; then
105
- npm ci
115
+ npm ci --workspaces=false
106
116
  else
107
- npm install --no-audit --no-fund
117
+ npm install --workspaces=false --no-audit --no-fund
108
118
  fi
109
119
 
110
120
  - name: Verify package
@@ -114,7 +124,7 @@ ${siblingPreparationCommands.split("\n").map((line) => ` ${line}`).join
114
124
  );
115
125
  return {
116
126
  cwd: options.workspaceRoot,
117
- args: ["act", options.eventName, "-W", workflowPath, "-j", "verify"]
127
+ args: createActArgs(options.eventName, workflowPath)
118
128
  };
119
129
  }
120
130
  function findWorkspaceRoot(packageRoot) {
@@ -186,7 +196,8 @@ function getTreeseedVerifyDriverStatus(options = {}) {
186
196
  const workflowPresent = existsSync(workflowPath);
187
197
  const workspace = resolveLocalWorkspaceContext(packageRoot);
188
198
  const checkCommand = options.checkCommand ?? check;
189
- const ghAct = checkCommand("gh", ["act", "--version"], packageRoot);
199
+ const gh = options.checkCommand ? "gh" : resolveTreeseedToolBinary("gh") ?? "gh";
200
+ const ghAct = checkCommand(gh, ["act", "--version"], packageRoot);
190
201
  const docker = checkCommand("docker", ["info"], packageRoot);
191
202
  const prefersDirectForLocalWorkspace = !inGitHubActions && driver === "auto" && workspace.localTreeseedSiblingDependencies.length > 0;
192
203
  return {
@@ -211,6 +222,7 @@ function runTreeseedVerifyDriver(options = {}) {
211
222
  const status = getTreeseedVerifyDriverStatus(options);
212
223
  const runCommand = options.runCommand ?? run;
213
224
  const checkCommand = options.checkCommand ?? check;
225
+ const gh = options.runCommand || options.checkCommand ? "gh" : resolveTreeseedToolBinary("gh") ?? "gh";
214
226
  if (status.driver === "direct" || status.inGitHubActions) {
215
227
  return runCommand("npm", ["run", "verify:direct"], status.packageRoot);
216
228
  }
@@ -220,7 +232,7 @@ function runTreeseedVerifyDriver(options = {}) {
220
232
  return 1;
221
233
  }
222
234
  if (!status.ghActAvailable) {
223
- const detail = checkCommand("gh", ["act", "--version"], status.packageRoot).detail;
235
+ const detail = checkCommand(gh, ["act", "--version"], status.packageRoot).detail;
224
236
  write(detail || "Treeseed verify requires `gh act` when TREESEED_VERIFY_DRIVER=act.", "stderr");
225
237
  return 1;
226
238
  }
@@ -236,15 +248,15 @@ function runTreeseedVerifyDriver(options = {}) {
236
248
  eventName: status.eventName,
237
249
  localTreeseedSiblingDependencies: status.localTreeseedSiblingDependencies
238
250
  });
239
- return runCommand("gh", workspaceAct.args, workspaceAct.cwd);
251
+ return runCommand(gh, workspaceAct.args, workspaceAct.cwd);
240
252
  }
241
- return runCommand("gh", ["act", status.eventName, "-W", ".github/workflows/verify.yml", "-j", "verify"], status.packageRoot);
253
+ return runCommand(gh, createActArgs(status.eventName, ".github/workflows/verify.yml"), status.packageRoot);
242
254
  }
243
255
  if (status.prefersDirectForLocalWorkspace) {
244
256
  return runCommand("npm", ["run", "verify:direct"], status.packageRoot);
245
257
  }
246
258
  if (status.canUseAct) {
247
- return runCommand("gh", ["act", status.eventName, "-W", ".github/workflows/verify.yml", "-j", "verify"], status.packageRoot);
259
+ return runCommand(gh, createActArgs(status.eventName, ".github/workflows/verify.yml"), status.packageRoot);
248
260
  }
249
261
  if (!status.workflowPresent) {
250
262
  write("Treeseed verify warning: package-local verify workflow is missing; falling back to verify:direct.", "stderr");