@stacksjs/buddy 0.70.257 → 0.70.259

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 (113) hide show
  1. package/dist/cli.d.ts +11 -1
  2. package/dist/cli.js +5 -164
  3. package/dist/commands/about.js +1 -35
  4. package/dist/commands/add.js +1 -25
  5. package/dist/commands/ai-context.js +4 -27
  6. package/dist/commands/auth.js +1 -68
  7. package/dist/commands/build.js +1 -221
  8. package/dist/commands/cd.js +8 -39
  9. package/dist/commands/changelog.js +1 -32
  10. package/dist/commands/clean.js +1 -44
  11. package/dist/commands/cloud.js +2 -519
  12. package/dist/commands/commit.js +1 -16
  13. package/dist/commands/completion.js +6 -42
  14. package/dist/commands/config-migrate.js +6 -94
  15. package/dist/commands/configure.js +2 -45
  16. package/dist/commands/create.js +1 -162
  17. package/dist/commands/deploy.d.ts +10 -4
  18. package/dist/commands/deploy.js +13 -1664
  19. package/dist/commands/desktop-apple.js +12 -399
  20. package/dist/commands/dev.js +6 -1083
  21. package/dist/commands/dns.js +3 -103
  22. package/dist/commands/docs/buddy-commands.js +10 -132
  23. package/dist/commands/docs/buddy-commands.test.js +1 -38
  24. package/dist/commands/docs/framework-repo.js +1 -14
  25. package/dist/commands/docs/framework-repo.test.js +1 -68
  26. package/dist/commands/docs/generated-artifacts.js +3 -70
  27. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  28. package/dist/commands/docs/links.js +2 -85
  29. package/dist/commands/docs/links.test.js +4 -66
  30. package/dist/commands/docs.js +1 -24
  31. package/dist/commands/doctor.js +2 -478
  32. package/dist/commands/domains.js +2 -152
  33. package/dist/commands/email.js +31 -503
  34. package/dist/commands/env.js +1 -312
  35. package/dist/commands/extension.js +1 -239
  36. package/dist/commands/features.js +9 -345
  37. package/dist/commands/fresh.js +1 -46
  38. package/dist/commands/generate.js +2 -152
  39. package/dist/commands/http.js +1 -33
  40. package/dist/commands/index.js +1 -57
  41. package/dist/commands/install.js +1 -22
  42. package/dist/commands/key.js +1 -23
  43. package/dist/commands/link.js +2 -147
  44. package/dist/commands/lint.js +2 -46
  45. package/dist/commands/list.js +1 -140
  46. package/dist/commands/mail.js +15 -1477
  47. package/dist/commands/maintenance.js +1 -141
  48. package/dist/commands/make.js +1 -392
  49. package/dist/commands/migrate-project.js +1 -49
  50. package/dist/commands/migrate.js +27 -625
  51. package/dist/commands/outdated.js +1 -19
  52. package/dist/commands/package.js +1 -17
  53. package/dist/commands/phone.js +17 -181
  54. package/dist/commands/ports.js +2 -118
  55. package/dist/commands/prepublish.js +1 -17
  56. package/dist/commands/projects.js +1 -29
  57. package/dist/commands/protocol/craft-evidence.js +4 -98
  58. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  59. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  60. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  61. package/dist/commands/protocol/desktop-support.js +2 -32
  62. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  63. package/dist/commands/protocol/driver-registry.js +2 -71
  64. package/dist/commands/protocol/pantry-evidence.js +4 -144
  65. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  66. package/dist/commands/protocol/release-manifest.js +3 -102
  67. package/dist/commands/protocol/release-manifest.test.js +2 -34
  68. package/dist/commands/protocol/run-conformance.js +4 -346
  69. package/dist/commands/protocol/run-conformance.test.js +1 -52
  70. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  71. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  72. package/dist/commands/protocol/source-manifest.js +3 -113
  73. package/dist/commands/protocol/sync-suite.js +3 -136
  74. package/dist/commands/protocol/sync-suite.test.js +1 -44
  75. package/dist/commands/protocol.js +1 -60
  76. package/dist/commands/publish.js +8 -500
  77. package/dist/commands/queue.js +1 -248
  78. package/dist/commands/release.js +1 -42
  79. package/dist/commands/route.js +1 -21
  80. package/dist/commands/run-tool.js +1 -9
  81. package/dist/commands/saas.js +1 -25
  82. package/dist/commands/schedule.js +1 -61
  83. package/dist/commands/search.js +1 -84
  84. package/dist/commands/seed.js +1 -47
  85. package/dist/commands/serve.js +1 -55
  86. package/dist/commands/setup-ai.js +2 -121
  87. package/dist/commands/setup.js +2 -305
  88. package/dist/commands/share.js +1 -214
  89. package/dist/commands/sms.js +32 -319
  90. package/dist/commands/stacks.js +1 -72
  91. package/dist/commands/telemetry.js +1 -74
  92. package/dist/commands/test.js +1 -130
  93. package/dist/commands/tinker.js +1 -37
  94. package/dist/commands/types.js +1 -18
  95. package/dist/commands/upgrade.js +1 -98
  96. package/dist/commands/user.js +3 -84
  97. package/dist/commands/version.js +1 -14
  98. package/dist/config.js +1 -231
  99. package/dist/custom-cli.js +1 -23
  100. package/dist/database-preflight.js +1 -88
  101. package/dist/global-options.js +1 -4
  102. package/dist/index.js +1 -1
  103. package/dist/lazy-commands.js +1 -241
  104. package/dist/migrators/index.js +2 -62
  105. package/dist/migrators/laravel/index.js +1 -148
  106. package/dist/migrators/laravel/migrations.js +4 -231
  107. package/dist/migrators/laravel/models.js +8 -111
  108. package/dist/migrators/rails/index.js +1 -11
  109. package/dist/production-server.js +1 -257
  110. package/dist/project-setup.js +1 -36
  111. package/dist/serve-entry.js +1 -5
  112. package/dist/version-info.js +1 -5
  113. package/package.json +54 -47
@@ -1,19 +1 @@
1
- import { $ } from "bun";
2
- import process from "node:process";
3
- import { log } from "@stacksjs/logging";
4
- import { projectPath } from "@stacksjs/path";
5
- import { onUnknownSubcommand } from "@stacksjs/cli";
6
- export function outdated(buddy) {
7
- const descriptions = {
8
- outdated: "List all the outdated project dependencies",
9
- project: "Target a specific project",
10
- verbose: "Enable verbose output"
11
- };
12
- buddy.command("outdated", descriptions.outdated).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async () => {
13
- log.debug("Running `buddy outdated` ...");
14
- $.cwd(projectPath());
15
- const response = await $`bun outdated`.text();
16
- console.log(response);
17
- });
18
- onUnknownSubcommand(buddy, "outdated");
19
- }
1
+ import{$}from"bun";import process from"node:process";import{log}from"@stacksjs/logging";import{projectPath}from"@stacksjs/path";import{onUnknownSubcommand}from"@stacksjs/cli";export function outdated(buddy){const descriptions={outdated:"List all the outdated project dependencies",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("outdated",descriptions.outdated).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async()=>{log.debug("Running `buddy outdated` ...");$.cwd(projectPath());const response=await $`bun outdated`.text();console.log(response)});onUnknownSubcommand(buddy,"outdated")}
@@ -1,17 +1 @@
1
- import process from "node:process";
2
- import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
3
- import { ExitCode } from "@stacksjs/types";
4
- export function packageCommands(buddy) {
5
- buddy.command("package:discover", "Discover Stacks-compatible packages from pantry").option("--verbose", "Enable verbose output", { default: !1 }).action(async () => {
6
- const perf = await intro("buddy package:discover"), { discoverPackages } = await import("@stacksjs/actions"), manifest = await discoverPackages(), count = Object.keys(manifest.packages).length;
7
- if (count > 0) {
8
- log.success(`Discovered ${count} Stacks package${count === 1 ? "" : "s"}:`);
9
- for (const name of Object.keys(manifest.packages))
10
- log.info(` - ${name}`);
11
- } else
12
- log.info("No Stacks-compatible packages found in pantry");
13
- await outro("Package discovery complete", { startTime: perf, useSeconds: !0 });
14
- process.exit(ExitCode.Success);
15
- });
16
- onUnknownSubcommand(buddy, "package");
17
- }
1
+ import process from"node:process";import{intro,log,onUnknownSubcommand,outro}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";export function packageCommands(buddy){buddy.command("package:discover","Discover Stacks-compatible packages from pantry").option("--verbose","Enable verbose output",{default:!1}).action(async()=>{const perf=await intro("buddy package:discover"),{discoverPackages}=await import("@stacksjs/actions"),manifest=await discoverPackages(),count=Object.keys(manifest.packages).length;if(count>0){log.success(`Discovered ${count} Stacks package${count===1?"":"s"}:`);for(const name of Object.keys(manifest.packages))log.info(` - ${name}`)}else log.info("No Stacks-compatible packages found in pantry");await outro("Package discovery complete",{startTime:perf,useSeconds:!0});process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"package")}
@@ -1,188 +1,24 @@
1
- import { getErrorMessage } from "@stacksjs/utils";
2
- import { readFileSync, existsSync } from "node:fs";
3
- const TIMEOUT_MS = 30000;
4
- async function withTimeout(promise, ms = TIMEOUT_MS) {
5
- let timeoutId;
6
- const timeoutPromise = new Promise((_, reject) => {
7
- timeoutId = setTimeout(() => reject(Error(`Operation timed out after ${ms}ms`)), ms);
8
- });
9
- try {
10
- return await Promise.race([promise, timeoutPromise]);
11
- } finally {
12
- clearTimeout(timeoutId);
13
- }
14
- }
15
- function loadAwsCredentials() {
16
- const envPath = ".env.production";
17
- if (existsSync(envPath)) {
18
- const content = readFileSync(envPath, "utf-8");
19
- for (const line of content.split(`
20
- `)) {
21
- const eq = line.indexOf("=");
22
- if (eq > 0 && !line.startsWith("#"))
23
- process.env[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
24
- }
25
- }
26
- }
27
- const descriptions = {
28
- phone: "Phone/voice service management commands",
29
- status: "Show phone service status",
30
- numbers: "List phone numbers",
31
- search: "Search available phone numbers",
32
- setup: "Initialize phone infrastructure"
33
- };
34
- export function phone(buddy) {
35
- buddy.command("phone", descriptions.phone).action(async () => {
36
- console.log(`
1
+ import{getErrorMessage}from"@stacksjs/utils";import{readFileSync,existsSync}from"node:fs";const TIMEOUT_MS=30000;async function withTimeout(promise,ms=TIMEOUT_MS){let timeoutId;const timeoutPromise=new Promise((_,reject)=>{timeoutId=setTimeout(()=>reject(Error(`Operation timed out after ${ms}ms`)),ms)});try{return await Promise.race([promise,timeoutPromise])}finally{clearTimeout(timeoutId)}}function loadAwsCredentials(){const envPath=".env.production";if(existsSync(envPath)){const content=readFileSync(envPath,"utf-8");for(const line of content.split(`
2
+ `)){const eq=line.indexOf("=");if(eq>0&&!line.startsWith("#"))process.env[line.slice(0,eq).trim()]=line.slice(eq+1).trim()}}}const descriptions={phone:"Phone/voice service management commands",status:"Show phone service status",numbers:"List phone numbers",search:"Search available phone numbers",setup:"Initialize phone infrastructure"};export function phone(buddy){buddy.command("phone",descriptions.phone).action(async()=>{console.log(`
37
3
  \uD83D\uDCDE Phone Service Commands
38
- `);
39
- console.log(" buddy phone:status - Show phone service status");
40
- console.log(" buddy phone:numbers - List phone numbers");
41
- console.log(" buddy phone:search - Search available phone numbers");
42
- console.log(" buddy phone:setup - Initialize phone infrastructure");
43
- console.log("");
44
- process.exit(0);
45
- });
46
- buddy.command("phone:status", descriptions.status).action(async () => {
47
- console.log(`
4
+ `);console.log(" buddy phone:status - Show phone service status");console.log(" buddy phone:numbers - List phone numbers");console.log(" buddy phone:search - Search available phone numbers");console.log(" buddy phone:setup - Initialize phone infrastructure");console.log("");process.exit(0)});buddy.command("phone:status",descriptions.status).action(async()=>{console.log(`
48
5
  \uD83D\uDCDE Phone Service Status
49
- `);
50
- loadAwsCredentials();
51
- try {
52
- const { ConnectClient } = await import("@stacksjs/ts-cloud"), connect = new ConnectClient(process.env.AWS_REGION || "us-east-1"), instanceAlias = `${(process.env.APP_NAME || "stacks").toLowerCase().replace(/[^a-z0-9-]/g, "-")}-phone`;
53
- console.log(`Looking for Connect instance: ${instanceAlias}`);
54
- const instance = (await withTimeout(connect.listInstances({ MaxResults: 100 }))).InstanceSummaryList?.find((i) => i.InstanceAlias === instanceAlias);
55
- if (instance) {
56
- console.log(`
6
+ `);loadAwsCredentials();try{const{ConnectClient}=await import("@stacksjs/ts-cloud"),connect=new ConnectClient(process.env.AWS_REGION||"us-east-1"),instanceAlias=`${(process.env.APP_NAME||"stacks").toLowerCase().replace(/[^a-z0-9-]/g,"-")}-phone`;console.log(`Looking for Connect instance: ${instanceAlias}`);const instance=(await withTimeout(connect.listInstances({MaxResults:100}))).InstanceSummaryList?.find((i)=>i.InstanceAlias===instanceAlias);if(instance){console.log(`
57
7
  \u2705 Phone Service Active
58
- `);
59
- console.log(` Instance ID: ${instance.Id}`);
60
- console.log(` Alias: ${instance.InstanceAlias}`);
61
- console.log(` Status: ${instance.InstanceStatus}`);
62
- console.log(` Inbound Calls: ${instance.InboundCallsEnabled ? "Enabled" : "Disabled"}`);
63
- console.log(` Outbound Calls: ${instance.OutboundCallsEnabled ? "Enabled" : "Disabled"}`);
64
- if (instance.Id)
65
- try {
66
- const numbers = await withTimeout(connect.listPhoneNumbers({ InstanceId: instance.Id }));
67
- if (numbers.ListPhoneNumbersSummaryList && numbers.ListPhoneNumbersSummaryList.length > 0) {
68
- console.log(`
69
- Phone Numbers:`);
70
- for (const num of numbers.ListPhoneNumbersSummaryList)
71
- console.log(` \uD83D\uDCF1 ${num.PhoneNumber} (${num.PhoneNumberType})`);
72
- } else
73
- console.log(`
74
- No phone numbers claimed yet.`);
75
- } catch {
76
- console.log(`
77
- Could not retrieve phone numbers.`);
78
- }
79
- } else {
80
- console.log(`
81
- \u274C Phone service not deployed.`);
82
- console.log(`
83
- \uD83D\uDCA1 To set up phone service:`);
84
- console.log(" 1. Enable phone in config/phone.ts");
85
- console.log(" 2. Run `buddy deploy`");
86
- }
87
- } catch (error) {
88
- console.error("Error checking status:", getErrorMessage(error));
89
- }
90
- process.exit(0);
91
- });
92
- buddy.command("phone:numbers", descriptions.numbers).action(async () => {
93
- console.log(`
8
+ `);console.log(` Instance ID: ${instance.Id}`);console.log(` Alias: ${instance.InstanceAlias}`);console.log(` Status: ${instance.InstanceStatus}`);console.log(` Inbound Calls: ${instance.InboundCallsEnabled?"Enabled":"Disabled"}`);console.log(` Outbound Calls: ${instance.OutboundCallsEnabled?"Enabled":"Disabled"}`);if(instance.Id)try{const numbers=await withTimeout(connect.listPhoneNumbers({InstanceId:instance.Id}));if(numbers.ListPhoneNumbersSummaryList&&numbers.ListPhoneNumbersSummaryList.length>0){console.log(`
9
+ Phone Numbers:`);for(const num of numbers.ListPhoneNumbersSummaryList)console.log(` \uD83D\uDCF1 ${num.PhoneNumber} (${num.PhoneNumberType})`)}else console.log(`
10
+ No phone numbers claimed yet.`)}catch{console.log(`
11
+ Could not retrieve phone numbers.`)}}else{console.log(`
12
+ \u274C Phone service not deployed.`);console.log(`
13
+ \uD83D\uDCA1 To set up phone service:`);console.log(" 1. Enable phone in config/phone.ts");console.log(" 2. Run `buddy deploy`")}}catch(error){console.error("Error checking status:",getErrorMessage(error))}process.exit(0)});buddy.command("phone:numbers",descriptions.numbers).action(async()=>{console.log(`
94
14
  \uD83D\uDCDE Phone Numbers
95
- `);
96
- loadAwsCredentials();
97
- try {
98
- const { ConnectClient } = await import("@stacksjs/ts-cloud"), connect = new ConnectClient(process.env.AWS_REGION || "us-east-1"), instanceAlias = `${(process.env.APP_NAME || "stacks").toLowerCase().replace(/[^a-z0-9-]/g, "-")}-phone`, instance = (await withTimeout(connect.listInstances({ MaxResults: 100 }))).InstanceSummaryList?.find((i) => i.InstanceAlias === instanceAlias);
99
- if (!instance?.Id) {
100
- console.log("Phone service not deployed. Run `buddy phone:setup` first.");
101
- process.exit(0);
102
- return;
103
- }
104
- const numbers = await withTimeout(connect.listPhoneNumbers({ InstanceId: instance.Id }));
105
- if (!numbers.ListPhoneNumbersSummaryList || numbers.ListPhoneNumbersSummaryList.length === 0) {
106
- console.log("No phone numbers claimed.");
107
- console.log("\n\uD83D\uDCA1 Use `buddy phone:search` to find available numbers.");
108
- process.exit(0);
109
- return;
110
- }
111
- console.log(`Claimed Phone Numbers:
112
- `);
113
- for (const num of numbers.ListPhoneNumbersSummaryList) {
114
- console.log(` \uD83D\uDCF1 ${num.PhoneNumber}`);
115
- console.log(` Type: ${num.PhoneNumberType}`);
116
- console.log(` Country: ${num.PhoneNumberCountryCode}`);
117
- if (num.PhoneNumberDescription)
118
- console.log(` Description: ${num.PhoneNumberDescription}`);
119
- console.log("");
120
- }
121
- } catch (error) {
122
- console.error("Error listing numbers:", getErrorMessage(error));
123
- }
124
- process.exit(0);
125
- });
126
- buddy.command("phone:search [country]", descriptions.search).option("-t, --type <type>", "Phone number type (TOLL_FREE, DID)", { default: "TOLL_FREE" }).action(async (country, options) => {
127
- const countryCode = country?.toUpperCase() || "US", phoneType = options.type?.toUpperCase() || "TOLL_FREE";
128
- console.log(`
15
+ `);loadAwsCredentials();try{const{ConnectClient}=await import("@stacksjs/ts-cloud"),connect=new ConnectClient(process.env.AWS_REGION||"us-east-1"),instanceAlias=`${(process.env.APP_NAME||"stacks").toLowerCase().replace(/[^a-z0-9-]/g,"-")}-phone`,instance=(await withTimeout(connect.listInstances({MaxResults:100}))).InstanceSummaryList?.find((i)=>i.InstanceAlias===instanceAlias);if(!instance?.Id){console.log("Phone service not deployed. Run `buddy phone:setup` first.");process.exit(0);return}const numbers=await withTimeout(connect.listPhoneNumbers({InstanceId:instance.Id}));if(!numbers.ListPhoneNumbersSummaryList||numbers.ListPhoneNumbersSummaryList.length===0){console.log("No phone numbers claimed.");console.log("\n\uD83D\uDCA1 Use `buddy phone:search` to find available numbers.");process.exit(0);return}console.log(`Claimed Phone Numbers:
16
+ `);for(const num of numbers.ListPhoneNumbersSummaryList){console.log(` \uD83D\uDCF1 ${num.PhoneNumber}`);console.log(` Type: ${num.PhoneNumberType}`);console.log(` Country: ${num.PhoneNumberCountryCode}`);if(num.PhoneNumberDescription)console.log(` Description: ${num.PhoneNumberDescription}`);console.log("")}}catch(error){console.error("Error listing numbers:",getErrorMessage(error))}process.exit(0)});buddy.command("phone:search [country]",descriptions.search).option("-t, --type <type>","Phone number type (TOLL_FREE, DID)",{default:"TOLL_FREE"}).action(async(country,options)=>{const countryCode=country?.toUpperCase()||"US",phoneType=options.type?.toUpperCase()||"TOLL_FREE";console.log(`
129
17
  \uD83D\uDCDE Searching for ${phoneType} numbers in ${countryCode}...
130
- `);
131
- loadAwsCredentials();
132
- try {
133
- const { ConnectClient } = await import("@stacksjs/ts-cloud"), connect = new ConnectClient(process.env.AWS_REGION || "us-east-1"), instanceAlias = `${(process.env.APP_NAME || "stacks").toLowerCase().replace(/[^a-z0-9-]/g, "-")}-phone`, instance = (await withTimeout(connect.listInstances({ MaxResults: 100 }))).InstanceSummaryList?.find((i) => i.InstanceAlias === instanceAlias);
134
- if (!instance?.Arn) {
135
- console.log("Phone service not deployed. Run `buddy phone:setup` first.");
136
- process.exit(0);
137
- return;
138
- }
139
- const available = await withTimeout(connect.searchAvailablePhoneNumbers({
140
- TargetArn: instance.Arn,
141
- PhoneNumberCountryCode: countryCode,
142
- PhoneNumberType: phoneType,
143
- MaxResults: 10
144
- }));
145
- if (!available.AvailableNumbersList || available.AvailableNumbersList.length === 0) {
146
- console.log(`No ${phoneType} numbers available in ${countryCode}.`);
147
- console.log(`
148
- \uD83D\uDCA1 Try a different country or number type.`);
149
- process.exit(0);
150
- return;
151
- }
152
- console.log(`Available Phone Numbers:
153
- `);
154
- for (const num of available.AvailableNumbersList)
155
- console.log(` \uD83D\uDCF1 ${num.PhoneNumber}`);
156
- console.log("\n\uD83D\uDCA1 To claim a number, use `buddy phone:claim <number>`");
157
- } catch (error) {
158
- if (getErrorMessage(error).includes("not authorized")) {
159
- console.log("Not authorized to search phone numbers.");
160
- console.log("Make sure your AWS account has Amazon Connect permissions.");
161
- } else
162
- console.error("Error searching numbers:", getErrorMessage(error));
163
- }
164
- process.exit(0);
165
- });
166
- buddy.command("phone:setup", descriptions.setup).action(async () => {
167
- console.log(`
18
+ `);loadAwsCredentials();try{const{ConnectClient}=await import("@stacksjs/ts-cloud"),connect=new ConnectClient(process.env.AWS_REGION||"us-east-1"),instanceAlias=`${(process.env.APP_NAME||"stacks").toLowerCase().replace(/[^a-z0-9-]/g,"-")}-phone`,instance=(await withTimeout(connect.listInstances({MaxResults:100}))).InstanceSummaryList?.find((i)=>i.InstanceAlias===instanceAlias);if(!instance?.Arn){console.log("Phone service not deployed. Run `buddy phone:setup` first.");process.exit(0);return}const available=await withTimeout(connect.searchAvailablePhoneNumbers({TargetArn:instance.Arn,PhoneNumberCountryCode:countryCode,PhoneNumberType:phoneType,MaxResults:10}));if(!available.AvailableNumbersList||available.AvailableNumbersList.length===0){console.log(`No ${phoneType} numbers available in ${countryCode}.`);console.log(`
19
+ \uD83D\uDCA1 Try a different country or number type.`);process.exit(0);return}console.log(`Available Phone Numbers:
20
+ `);for(const num of available.AvailableNumbersList)console.log(` \uD83D\uDCF1 ${num.PhoneNumber}`);console.log("\n\uD83D\uDCA1 To claim a number, use `buddy phone:claim <number>`")}catch(error){if(getErrorMessage(error).includes("not authorized")){console.log("Not authorized to search phone numbers.");console.log("Make sure your AWS account has Amazon Connect permissions.")}else console.error("Error searching numbers:",getErrorMessage(error))}process.exit(0)});buddy.command("phone:setup",descriptions.setup).action(async()=>{console.log(`
168
21
  \uD83D\uDCDE Phone Service Setup
169
- `);
170
- console.log("Amazon Connect setup requires manual configuration.");
171
- console.log(`
22
+ `);console.log("Amazon Connect setup requires manual configuration.");console.log(`
172
23
  Steps to set up phone service:
173
- `);
174
- console.log("1. Enable phone in config/phone.ts:");
175
- console.log(" enabled: true");
176
- console.log("");
177
- console.log("2. Run `buddy deploy` to create the Connect instance");
178
- console.log("");
179
- console.log("3. Access the Connect console to:");
180
- console.log(" - Claim phone numbers");
181
- console.log(" - Configure contact flows");
182
- console.log(" - Set up routing profiles");
183
- console.log("");
184
- console.log("4. Use `buddy phone:status` to verify setup");
185
- console.log("");
186
- process.exit(0);
187
- });
188
- }
24
+ `);console.log("1. Enable phone in config/phone.ts:");console.log(" enabled: true");console.log("");console.log("2. Run `buddy deploy` to create the Connect instance");console.log("");console.log("3. Access the Connect console to:");console.log(" - Claim phone numbers");console.log(" - Configure contact flows");console.log(" - Set up routing profiles");console.log("");console.log("4. Use `buddy phone:status` to verify setup");console.log("");process.exit(0)})}
@@ -1,118 +1,2 @@
1
- import { $ } from "bun";
2
- import process from "node:process";
3
- import { intro, italic, onUnknownSubcommand, outro } from "@stacksjs/cli";
4
- import { ports as projectPorts } from "@stacksjs/config";
5
- import { log } from "@stacksjs/logging";
6
- import { findProjectPath, path as p, projectPath } from "@stacksjs/path";
7
- import { ExitCode } from "@stacksjs/types";
8
- import { findStacksProjects } from "@stacksjs/utils";
9
- export function ports(buddy) {
10
- const descriptions = {
11
- command: "Let buddy check your project for potential issues and misconfigurations",
12
- ports: "Check if the ports are available",
13
- project: "Target a specific project",
14
- verbose: "Enable verbose output"
15
- };
16
- buddy.command("ports", descriptions.command).option("-l, --list", "List the used ports", { default: !0 }).option("-c, --check", "Check if the ports are available", {
17
- default: !1
18
- }).option("-p, --project [name]", descriptions.project, {
19
- default: void 0
20
- }).option("-q, --quiet", "Use minimal output", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
21
- log.debug("Running `buddy ports` ...", options);
22
- let perf;
23
- if (!options.quiet)
24
- perf = await intro("buddy ports");
25
- if (options.project) {
26
- const path = await findProjectPath(options.project);
27
- log.debug(`Path: ${path}`);
28
- const ports = await getPortsForProjectPath(path, options);
29
- log.debug(`./buddy ports --quiet response for ${projectPath}`, ports);
30
- outputPorts(ports, options);
31
- } else
32
- outputPorts(projectPorts, options);
33
- if (!options.quiet)
34
- await outro("Exited", { startTime: perf, useSeconds: !1 });
35
- process.exit(ExitCode.Success);
36
- });
37
- buddy.command("ports:list", descriptions.ports).option("-p, --project [name]", descriptions.project, {
38
- default: void 0
39
- }).option("-q, --quiet", "Use minimal output", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
40
- log.debug("Running `buddy ports:list` ...", options);
41
- let perf;
42
- if (!options.quiet)
43
- perf = await intro("buddy ports:list");
44
- if (options.project) {
45
- if (!options.quiet)
46
- log.info(`Listing ports for project: ${italic(options.project)}`);
47
- const path = await findProjectPath(options.project);
48
- log.debug(`Path: ${path}`);
49
- const ports = await getPortsForProjectPath(path, options);
50
- outputPorts(ports, options);
51
- } else
52
- outputPorts(projectPorts, options);
53
- if (!options.quiet)
54
- await outro("Exited", { startTime: perf, useSeconds: !1 });
55
- process.exit(ExitCode.Success);
56
- });
57
- buddy.command("ports:check", descriptions.ports).option("-p, --project [name]", descriptions.project, {
58
- default: projectPath()
59
- }).option("-q, --quiet", "Use minimal output", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
60
- log.debug("Running `buddy ports:check` ...", options);
61
- let perf;
62
- if (!options.quiet)
63
- perf = await intro("buddy ports:check");
64
- const projects = await findStacksProjects(void 0, { quiet: !0 });
65
- log.debug("Running `buddy ports`");
66
- log.debug(`Found ${projects.length} projects`);
67
- log.debug("Projects:", projects);
68
- const projectsPorts = {};
69
- for (const project of projects)
70
- projectsPorts[project] = await getPortsForProjectPath(project, options);
71
- log.info("ProjectsPorts:", projectsPorts);
72
- if (!options.quiet)
73
- await outro("Exited", { startTime: perf, useSeconds: !1 });
74
- process.exit(ExitCode.Success);
75
- });
76
- onUnknownSubcommand(buddy, "ports");
77
- }
78
- async function getPortsForProjectPath(path, options) {
79
- if (!options.quiet)
80
- log.info(`Checking ports for project: ${italic(path)}`);
81
- $.cwd(path);
82
- try {
83
- $.env(await import(`${path}/.env`));
84
- } catch (err) {
85
- if (!options.quiet)
86
- log.warn(`[ports] Failed to load ${path}/.env (${err.message}). Falling back to current environment.`);
87
- }
88
- const projectList = await $`./buddy projects:list --quiet`.text();
89
- log.debug("ProjectListResponse", projectList);
90
- const projects = projectList.split(`
91
- `).filter((line) => line.startsWith(" - ")).map((line) => line.trim().substring(4));
92
- log.debug("Projects:", projects);
93
- const ppath = options.project ?? p.projectPath();
94
- let projectPath = projects.find((project) => project.includes(ppath));
95
- log.debug(`Checking ports for project: ${projectPath}`);
96
- if (projectPath === "" || !projectPath)
97
- projectPath = p.projectPath();
98
- log.debug(`$ Running: ./buddy ports:list --quiet via ${projectPath}`);
99
- const response = await $`./buddy ports:list --quiet`.json();
100
- log.debug(`Response for ./buddy ports:list --quiet via ${projectPath}`, response);
101
- let validJsonString = response.replace(/(\w+)(?=\s*:)/g, '"$1"');
102
- validJsonString = validJsonString.replace(/,(\s*\})/g, "$1");
103
- let ports;
104
- try {
105
- ports = JSON.parse(validJsonString);
106
- } catch {
107
- throw Error(`Failed to parse ports configuration: ${validJsonString.slice(0, 200)}`);
108
- }
109
- log.debug(`Ports for ${projectPath}`, ports);
110
- return ports;
111
- }
112
- function outputPorts(ports, options) {
113
- if (!options.quiet)
114
- console.log("");
115
- console.log(ports);
116
- if (!options.quiet)
117
- console.log("");
118
- }
1
+ import{$}from"bun";import process from"node:process";import{intro,italic,onUnknownSubcommand,outro}from"@stacksjs/cli";import{ports as projectPorts}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{findProjectPath,path as p,projectPath}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{findStacksProjects}from"@stacksjs/utils";export function ports(buddy){const descriptions={command:"Let buddy check your project for potential issues and misconfigurations",ports:"Check if the ports are available",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("ports",descriptions.command).option("-l, --list","List the used ports",{default:!0}).option("-c, --check","Check if the ports are available",{default:!1}).option("-p, --project [name]",descriptions.project,{default:void 0}).option("-q, --quiet","Use minimal output",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy ports` ...",options);let perf;if(!options.quiet)perf=await intro("buddy ports");if(options.project){const path=await findProjectPath(options.project);log.debug(`Path: ${path}`);const ports=await getPortsForProjectPath(path,options);log.debug(`./buddy ports --quiet response for ${projectPath}`,ports);outputPorts(ports,options)}else outputPorts(projectPorts,options);if(!options.quiet)await outro("Exited",{startTime:perf,useSeconds:!1});process.exit(ExitCode.Success)});buddy.command("ports:list",descriptions.ports).option("-p, --project [name]",descriptions.project,{default:void 0}).option("-q, --quiet","Use minimal output",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy ports:list` ...",options);let perf;if(!options.quiet)perf=await intro("buddy ports:list");if(options.project){if(!options.quiet)log.info(`Listing ports for project: ${italic(options.project)}`);const path=await findProjectPath(options.project);log.debug(`Path: ${path}`);const ports=await getPortsForProjectPath(path,options);outputPorts(ports,options)}else outputPorts(projectPorts,options);if(!options.quiet)await outro("Exited",{startTime:perf,useSeconds:!1});process.exit(ExitCode.Success)});buddy.command("ports:check",descriptions.ports).option("-p, --project [name]",descriptions.project,{default:projectPath()}).option("-q, --quiet","Use minimal output",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy ports:check` ...",options);let perf;if(!options.quiet)perf=await intro("buddy ports:check");const projects=await findStacksProjects(void 0,{quiet:!0});log.debug("Running `buddy ports`");log.debug(`Found ${projects.length} projects`);log.debug("Projects:",projects);const projectsPorts={};for(const project of projects)projectsPorts[project]=await getPortsForProjectPath(project,options);log.info("ProjectsPorts:",projectsPorts);if(!options.quiet)await outro("Exited",{startTime:perf,useSeconds:!1});process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"ports")}async function getPortsForProjectPath(path,options){if(!options.quiet)log.info(`Checking ports for project: ${italic(path)}`);$.cwd(path);try{$.env(await import(`${path}/.env`))}catch(err){if(!options.quiet)log.warn(`[ports] Failed to load ${path}/.env (${err.message}). Falling back to current environment.`)}const projectList=await $`./buddy projects:list --quiet`.text();log.debug("ProjectListResponse",projectList);const projects=projectList.split(`
2
+ `).filter((line)=>line.startsWith(" - ")).map((line)=>line.trim().substring(4));log.debug("Projects:",projects);const ppath=options.project??p.projectPath();let projectPath=projects.find((project)=>project.includes(ppath));log.debug(`Checking ports for project: ${projectPath}`);if(projectPath===""||!projectPath)projectPath=p.projectPath();log.debug(`$ Running: ./buddy ports:list --quiet via ${projectPath}`);const response=await $`./buddy ports:list --quiet`.json();log.debug(`Response for ./buddy ports:list --quiet via ${projectPath}`,response);let validJsonString=response.replace(/(\w+)(?=\s*:)/g,'"$1"');validJsonString=validJsonString.replace(/,(\s*\})/g,"$1");let ports;try{ports=JSON.parse(validJsonString)}catch{throw Error(`Failed to parse ports configuration: ${validJsonString.slice(0,200)}`)}log.debug(`Ports for ${projectPath}`,ports);return ports}function outputPorts(ports,options){if(!options.quiet)console.log("");console.log(ports);if(!options.quiet)console.log("")}
@@ -1,17 +1 @@
1
- import process from "node:process";
2
- import { runAction } from "@stacksjs/actions";
3
- import { Action } from "@stacksjs/enums";
4
- import { log } from "@stacksjs/logging";
5
- import { onUnknownSubcommand } from "@stacksjs/cli";
6
- export function prepublish(buddy) {
7
- const descriptions = {
8
- command: "Run your prepublish script",
9
- project: "Target a specific project",
10
- verbose: "Enable verbose output"
11
- };
12
- buddy.command("prepublish", descriptions.command).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
13
- log.debug("Running `buddy prepublish` ...", options);
14
- await runAction(Action.Prepublish, options);
15
- });
16
- onUnknownSubcommand(buddy, "prepublish");
17
- }
1
+ import process from"node:process";import{runAction}from"@stacksjs/actions";import{Action}from"@stacksjs/enums";import{log}from"@stacksjs/logging";import{onUnknownSubcommand}from"@stacksjs/cli";export function prepublish(buddy){const descriptions={command:"Run your prepublish script",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("prepublish",descriptions.command).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy prepublish` ...",options);await runAction(Action.Prepublish,options)});onUnknownSubcommand(buddy,"prepublish")}
@@ -1,29 +1 @@
1
- import process from "node:process";
2
- import { intro, log, onUnknownSubcommand } from "@stacksjs/cli";
3
- import { ExitCode } from "@stacksjs/types";
4
- import { findStacksProjects } from "@stacksjs/utils";
5
- export function projects(buddy) {
6
- const descriptions = {
7
- projects: "Find all Stacks projects on your system",
8
- verbose: "Enable verbose output"
9
- };
10
- buddy.command("projects", descriptions.projects).option("-q, --quiet", "Use minimal output", { default: !1 }).option("-l, --list", "List all local Stacks projects", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
11
- log.debug("Running `buddy projects` ...", options);
12
- if (!options.quiet)
13
- await intro("buddy projects");
14
- const projects = await findStacksProjects(void 0, options);
15
- for (const project of projects)
16
- console.log(" - ", project);
17
- process.exit(ExitCode.Success);
18
- });
19
- buddy.command("projects:list", descriptions.projects).option("-q, --quiet", "Use minimal output", { default: !1 }).option("-l, --list", "List all local Stacks projects", { default: !0 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
20
- log.debug("Running `buddy projects` ...", options);
21
- if (!options.quiet)
22
- await intro("buddy projects:list");
23
- const projects = await findStacksProjects(void 0, options);
24
- for (const project of projects)
25
- console.log(" - ", project);
26
- process.exit(ExitCode.Success);
27
- });
28
- onUnknownSubcommand(buddy, "projects");
29
- }
1
+ import process from"node:process";import{intro,log,onUnknownSubcommand}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";import{findStacksProjects}from"@stacksjs/utils";export function projects(buddy){const descriptions={projects:"Find all Stacks projects on your system",verbose:"Enable verbose output"};buddy.command("projects",descriptions.projects).option("-q, --quiet","Use minimal output",{default:!1}).option("-l, --list","List all local Stacks projects",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy projects` ...",options);if(!options.quiet)await intro("buddy projects");const projects=await findStacksProjects(void 0,options);for(const project of projects)console.log(" - ",project);process.exit(ExitCode.Success)});buddy.command("projects:list",descriptions.projects).option("-q, --quiet","Use minimal output",{default:!1}).option("-l, --list","List all local Stacks projects",{default:!0}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy projects` ...",options);if(!options.quiet)await intro("buddy projects:list");const projects=await findStacksProjects(void 0,options);for(const project of projects)console.log(" - ",project);process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"projects")}
@@ -1,98 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/craft.json");
4
- export const craftEvidence = {
5
- schemaVersion: 1,
6
- status: "source-verified-ci-retained",
7
- source: {
8
- repository: "https://github.com/home-lang/craft",
9
- version: "0.0.48",
10
- tag: "v0.0.48",
11
- revision: "bf75807669e3af6cfb4ae34724be5c613442aee2"
12
- },
13
- issues: {
14
- support: "https://github.com/stacksjs/stacks/issues/2059",
15
- signing: "https://github.com/stacksjs/stacks/issues/2062",
16
- lifecycle: "https://github.com/stacksjs/stacks/issues/2063",
17
- upstreamActions: "https://github.com/home-lang/craft/issues/11",
18
- dmgContention: "https://github.com/home-lang/craft/issues/16"
19
- },
20
- contracts: [
21
- { path: "packages/typescript/src/package.ts", sha256: "a7522f8c79dbd50a0f3bbc113525fb46eb8d6d1bab711d434cae85dc892ccdc1" },
22
- { path: "packages/typescript/src/package.test.ts", sha256: "673338f3793e397a83590daf84815869b0698b4afe97eaad8039426418554b7a" },
23
- { path: "scripts/native-lifecycle.ts", sha256: "a2c0af2c2165981ca7fbd31880adeec64db64fc305d4ca3efe62a11483b289b4" },
24
- { path: "scripts/native-lifecycle-plan.ts", sha256: "ac8e8f101246e94c0c26fdcbf9202536426bf192a303606079273d9689e4e27d" },
25
- { path: "scripts/native-lifecycle-plan.test.ts", sha256: "5cc839b60f7c8e32716f29201b159af817f4ac40b2c7963131124b3ac5de0638" },
26
- { path: ".github/workflows/native-lifecycle.yml", sha256: "493b614554f03dc669724ff31bd56ae8361851dd2a9b32ac37560d9c0bb1b85e" },
27
- { path: ".github/workflows/release.yml", sha256: "e3c7edd45841a7260fb4dfe810f0ff3f63f3117f7d0f4d14984e4dfb36b1f638" }
28
- ],
29
- lifecycleMatrix: [
30
- { runner: "macos-15", platform: "darwin", architecture: "arm64", formats: ["dmg", "pkg"] },
31
- { runner: "macos-15-intel", platform: "darwin", architecture: "x64", formats: ["dmg", "pkg"] },
32
- { runner: "ubuntu-24.04", platform: "linux", architecture: "x64", formats: ["deb"] },
33
- { runner: "windows-2025", platform: "win32", architecture: "x64", formats: ["msi", "zip"] }
34
- ],
35
- lifecycleOperations: ["install", "launch", "update", "rollback", "uninstall"],
36
- stacksWorkflow: "https://github.com/stacksjs/stacks/actions/workflows/desktop-lifecycle.yml",
37
- release: {
38
- sourceTagged: !0,
39
- githubRelease: null,
40
- packagePublished: !1,
41
- signing: "pending",
42
- notarization: "pending",
43
- blockedBy: "https://github.com/home-lang/craft/issues/11"
44
- },
45
- verification: {
46
- local: ["bun test packages/typescript/src/package.test.ts scripts/native-lifecycle-plan.test.ts", "bun run typecheck", "bun .github/scripts/native-lifecycle.ts"],
47
- retained: "https://github.com/stacksjs/stacks/actions/runs/29890804159"
48
- }
49
- };
50
- export function validateCraftEvidence(evidence) {
51
- const errors = [];
52
- if (evidence.source.tag !== `v${evidence.source.version}`)
53
- errors.push("Craft tag does not match its version");
54
- if (!/^[a-f0-9]{40}$/.test(evidence.source.revision))
55
- errors.push("Craft revision is not a full commit SHA");
56
- if (evidence.contracts.length !== 7)
57
- errors.push("all Craft packaging/release contracts must be pinned");
58
- for (const contract of evidence.contracts)
59
- if (!/^[a-f0-9]{64}$/.test(contract.sha256))
60
- errors.push(`${contract.path}: invalid SHA-256 digest`);
61
- const targets = new Set(evidence.lifecycleMatrix.map((row) => `${row.platform}/${row.architecture}`));
62
- for (const target of ["darwin/arm64", "darwin/x64", "linux/x64", "win32/x64"])
63
- if (!targets.has(target))
64
- errors.push(`Craft lifecycle matrix is missing ${target}`);
65
- if (evidence.release.signing !== "pending" || evidence.release.notarization !== "pending")
66
- errors.push("Craft signing/notarization cannot be promoted without retained release evidence");
67
- if (!evidence.release.blockedBy)
68
- errors.push("Craft release blocker must be linked");
69
- return errors;
70
- }
71
- function serializedEvidence() {
72
- return `${JSON.stringify(craftEvidence, null, 2)}
73
- `;
74
- }
75
- export async function run() {
76
- const errors = validateCraftEvidence(craftEvidence);
77
- if (process.argv.includes("--write")) {
78
- if (errors.length)
79
- throw Error(errors.join(`
80
- `));
81
- writeFileSync(outputPath, serializedEvidence());
82
- console.log(`Wrote Craft evidence for ${craftEvidence.source.tag}`);
83
- } else if (process.argv.includes("--check")) {
84
- if (!existsSync(outputPath))
85
- errors.push(".github/protocol/evidence/craft.json is missing");
86
- else if (readFileSync(outputPath, "utf8") !== serializedEvidence())
87
- errors.push("Craft evidence is stale; run bun run protocol:craft");
88
- if (errors.length)
89
- throw Error(errors.join(`
90
- `));
91
- console.log(`Craft evidence pins ${craftEvidence.source.tag} at ${craftEvidence.source.revision}`);
92
- } else {
93
- console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts --write | --check");
94
- process.exit(2);
95
- }
96
- }
97
- if (import.meta.main)
98
- await run();
1
+ import{existsSync,readFileSync,writeFileSync}from"node:fs";import{resolve}from"node:path";const root=resolve(import.meta.dir,"../../../../../../.."),outputPath=resolve(root,".github/protocol/evidence/craft.json");export const craftEvidence={schemaVersion:1,status:"source-verified-ci-retained",source:{repository:"https://github.com/home-lang/craft",version:"0.0.48",tag:"v0.0.48",revision:"bf75807669e3af6cfb4ae34724be5c613442aee2"},issues:{support:"https://github.com/stacksjs/stacks/issues/2059",signing:"https://github.com/stacksjs/stacks/issues/2062",lifecycle:"https://github.com/stacksjs/stacks/issues/2063",upstreamActions:"https://github.com/home-lang/craft/issues/11",dmgContention:"https://github.com/home-lang/craft/issues/16"},contracts:[{path:"packages/typescript/src/package.ts",sha256:"a7522f8c79dbd50a0f3bbc113525fb46eb8d6d1bab711d434cae85dc892ccdc1"},{path:"packages/typescript/src/package.test.ts",sha256:"673338f3793e397a83590daf84815869b0698b4afe97eaad8039426418554b7a"},{path:"scripts/native-lifecycle.ts",sha256:"a2c0af2c2165981ca7fbd31880adeec64db64fc305d4ca3efe62a11483b289b4"},{path:"scripts/native-lifecycle-plan.ts",sha256:"ac8e8f101246e94c0c26fdcbf9202536426bf192a303606079273d9689e4e27d"},{path:"scripts/native-lifecycle-plan.test.ts",sha256:"5cc839b60f7c8e32716f29201b159af817f4ac40b2c7963131124b3ac5de0638"},{path:".github/workflows/native-lifecycle.yml",sha256:"493b614554f03dc669724ff31bd56ae8361851dd2a9b32ac37560d9c0bb1b85e"},{path:".github/workflows/release.yml",sha256:"e3c7edd45841a7260fb4dfe810f0ff3f63f3117f7d0f4d14984e4dfb36b1f638"}],lifecycleMatrix:[{runner:"macos-15",platform:"darwin",architecture:"arm64",formats:["dmg","pkg"]},{runner:"macos-15-intel",platform:"darwin",architecture:"x64",formats:["dmg","pkg"]},{runner:"ubuntu-24.04",platform:"linux",architecture:"x64",formats:["deb"]},{runner:"windows-2025",platform:"win32",architecture:"x64",formats:["msi","zip"]}],lifecycleOperations:["install","launch","update","rollback","uninstall"],stacksWorkflow:"https://github.com/stacksjs/stacks/actions/workflows/desktop-lifecycle.yml",release:{sourceTagged:!0,githubRelease:null,packagePublished:!1,signing:"pending",notarization:"pending",blockedBy:"https://github.com/home-lang/craft/issues/11"},verification:{local:["bun test packages/typescript/src/package.test.ts scripts/native-lifecycle-plan.test.ts","bun run typecheck","bun .github/scripts/native-lifecycle.ts"],retained:"https://github.com/stacksjs/stacks/actions/runs/29890804159"}};export function validateCraftEvidence(evidence){const errors=[];if(evidence.source.tag!==`v${evidence.source.version}`)errors.push("Craft tag does not match its version");if(!/^[a-f0-9]{40}$/.test(evidence.source.revision))errors.push("Craft revision is not a full commit SHA");if(evidence.contracts.length!==7)errors.push("all Craft packaging/release contracts must be pinned");for(const contract of evidence.contracts)if(!/^[a-f0-9]{64}$/.test(contract.sha256))errors.push(`${contract.path}: invalid SHA-256 digest`);const targets=new Set(evidence.lifecycleMatrix.map((row)=>`${row.platform}/${row.architecture}`));for(const target of["darwin/arm64","darwin/x64","linux/x64","win32/x64"])if(!targets.has(target))errors.push(`Craft lifecycle matrix is missing ${target}`);if(evidence.release.signing!=="pending"||evidence.release.notarization!=="pending")errors.push("Craft signing/notarization cannot be promoted without retained release evidence");if(!evidence.release.blockedBy)errors.push("Craft release blocker must be linked");return errors}function serializedEvidence(){return`${JSON.stringify(craftEvidence,null,2)}
2
+ `}export async function run(){const errors=validateCraftEvidence(craftEvidence);if(process.argv.includes("--write")){if(errors.length)throw Error(errors.join(`
3
+ `));writeFileSync(outputPath,serializedEvidence());console.log(`Wrote Craft evidence for ${craftEvidence.source.tag}`)}else if(process.argv.includes("--check")){if(!existsSync(outputPath))errors.push(".github/protocol/evidence/craft.json is missing");else if(readFileSync(outputPath,"utf8")!==serializedEvidence())errors.push("Craft evidence is stale; run bun run protocol:craft");if(errors.length)throw Error(errors.join(`
4
+ `));console.log(`Craft evidence pins ${craftEvidence.source.tag} at ${craftEvidence.source.revision}`)}else{console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts --write | --check");process.exit(2)}}if(import.meta.main)await run();
@@ -1,20 +1 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { craftEvidence, validateCraftEvidence } from "./craft-evidence";
3
- describe("Craft protocol evidence", () => {
4
- it("pins the full native lifecycle and release contract", () => {
5
- expect(validateCraftEvidence(craftEvidence)).toEqual([]);
6
- expect(craftEvidence.lifecycleMatrix).toHaveLength(4);
7
- expect(craftEvidence.lifecycleOperations).toEqual(["install", "launch", "update", "rollback", "uninstall"]);
8
- });
9
- it("rejects untraceable source and premature signing claims", () => {
10
- const invalid = structuredClone(craftEvidence);
11
- Object.assign(invalid.source, { tag: "latest", revision: "short" });
12
- Object.assign(invalid.release, { signing: "enforced", notarization: "enforced", blockedBy: "" });
13
- expect(validateCraftEvidence(invalid)).toEqual(expect.arrayContaining([
14
- "Craft tag does not match its version",
15
- "Craft revision is not a full commit SHA",
16
- "Craft signing/notarization cannot be promoted without retained release evidence",
17
- "Craft release blocker must be linked"
18
- ]));
19
- });
20
- });
1
+ import{describe,expect,it}from"bun:test";import{craftEvidence,validateCraftEvidence}from"./craft-evidence";describe("Craft protocol evidence",()=>{it("pins the full native lifecycle and release contract",()=>{expect(validateCraftEvidence(craftEvidence)).toEqual([]);expect(craftEvidence.lifecycleMatrix).toHaveLength(4);expect(craftEvidence.lifecycleOperations).toEqual(["install","launch","update","rollback","uninstall"])});it("rejects untraceable source and premature signing claims",()=>{const invalid=structuredClone(craftEvidence);Object.assign(invalid.source,{tag:"latest",revision:"short"});Object.assign(invalid.release,{signing:"enforced",notarization:"enforced",blockedBy:""});expect(validateCraftEvidence(invalid)).toEqual(expect.arrayContaining(["Craft tag does not match its version","Craft revision is not a full commit SHA","Craft signing/notarization cannot be promoted without retained release evidence","Craft release blocker must be linked"]))})});