@ashlr/stack 0.3.1 → 0.3.7

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 (2) hide show
  1. package/dist/index.js +73 -17
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1490,9 +1490,9 @@ var init_stack_manifest = __esm(() => {
1490
1490
  schema_version: 1,
1491
1491
  kind: "ashlr-stack-manifest",
1492
1492
  components: Object.freeze([
1493
- Object.freeze({ id: "core", name: "@ashlr/stack-core", version: "0.3.1", distribution: "npm" }),
1494
- Object.freeze({ id: "cli", name: "@ashlr/stack", version: "0.3.1", distribution: "npm" }),
1495
- Object.freeze({ id: "mcp", name: "ashlr-stack-mcp", version: "0.3.1", distribution: "npm" }),
1493
+ Object.freeze({ id: "core", name: "@ashlr/stack-core", version: "0.3.7", distribution: "npm" }),
1494
+ Object.freeze({ id: "cli", name: "@ashlr/stack", version: "0.3.7", distribution: "npm" }),
1495
+ Object.freeze({ id: "mcp", name: "ashlr-stack-mcp", version: "0.3.7", distribution: "npm" }),
1496
1496
  Object.freeze({
1497
1497
  id: "plugin",
1498
1498
  name: "stack",
@@ -1748,7 +1748,7 @@ async function exec(args, opts = {}) {
1748
1748
  return new Promise((resolvePromise, reject) => {
1749
1749
  const hasTimeout = typeof opts.timeoutMs === "number" && Number.isFinite(opts.timeoutMs);
1750
1750
  const useGroup = hasTimeout && !opts.interactive && process.platform !== "win32";
1751
- const child = spawn(binary, args, {
1751
+ const child = spawnImpl(binary, args, {
1752
1752
  env,
1753
1753
  cwd: opts.cwd,
1754
1754
  stdio: opts.interactive ? "inherit" : ["pipe", "pipe", "pipe"],
@@ -1940,7 +1940,7 @@ function formatArgsForError(args) {
1940
1940
  });
1941
1941
  return redacted.join(" ");
1942
1942
  }
1943
- var PhantomTimeoutError, cachedInstalled;
1943
+ var PhantomTimeoutError, cachedInstalled, spawnImpl;
1944
1944
  var init_phantom = __esm(() => {
1945
1945
  init_errors();
1946
1946
  init_phantom_process();
@@ -1950,6 +1950,7 @@ var init_phantom = __esm(() => {
1950
1950
  this.name = "PhantomTimeoutError";
1951
1951
  }
1952
1952
  };
1953
+ spawnImpl = spawn;
1953
1954
  });
1954
1955
  // ../core/src/env.ts
1955
1956
  function resolveOAuthClientId(provider, envOverride) {
@@ -6663,7 +6664,7 @@ import { mkdir as mkdir3, writeFile as writeFile6 } from "node:fs/promises";
6663
6664
  import { dirname as dirname3, resolve as resolve8 } from "node:path";
6664
6665
  async function wirePhantomForRecipe(recipe, opts = {}) {
6665
6666
  const result = { present: [], missing: [], webhooks: [], skipped: [] };
6666
- if (opts.noWire)
6667
+ if (opts.noWire || recipe.providers.length === 0)
6667
6668
  return result;
6668
6669
  const cwd = opts.cwd ?? process.cwd();
6669
6670
  try {
@@ -6784,7 +6785,10 @@ function signatureHeaderFor(provider) {
6784
6785
  }
6785
6786
  }
6786
6787
  function capitalize(s) {
6787
- return s.charAt(0).toUpperCase() + s.slice(1);
6788
+ if (s === "")
6789
+ return s;
6790
+ const first = String.fromCodePoint(s.codePointAt(0));
6791
+ return first.toUpperCase() + s.slice(first.length);
6788
6792
  }
6789
6793
  var WEBHOOK_PROVIDERS;
6790
6794
  var init_phantom_wire = __esm(() => {
@@ -9139,7 +9143,7 @@ var init_dist2 = __esm(() => {
9139
9143
  const e = i(true);
9140
9144
  return Object.keys(e);
9141
9145
  } });
9142
- t = typeof process < "u" && process.env && "development" || "";
9146
+ t = typeof process < "u" && process.env && "production" || "";
9143
9147
  f2 = [["APPVEYOR"], ["AWS_AMPLIFY", "AWS_APP_ID", { ci: true }], ["AZURE_PIPELINES", "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"], ["AZURE_STATIC", "INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN"], ["APPCIRCLE", "AC_APPCIRCLE"], ["BAMBOO", "bamboo_planKey"], ["BITBUCKET", "BITBUCKET_COMMIT"], ["BITRISE", "BITRISE_IO"], ["BUDDY", "BUDDY_WORKSPACE_ID"], ["BUILDKITE"], ["CIRCLE", "CIRCLECI"], ["CIRRUS", "CIRRUS_CI"], ["CLOUDFLARE_PAGES", "CF_PAGES", { ci: true }], ["CODEBUILD", "CODEBUILD_BUILD_ARN"], ["CODEFRESH", "CF_BUILD_ID"], ["DRONE"], ["DRONE", "DRONE_BUILD_EVENT"], ["DSARI"], ["GITHUB_ACTIONS"], ["GITLAB", "GITLAB_CI"], ["GITLAB", "CI_MERGE_REQUEST_ID"], ["GOCD", "GO_PIPELINE_LABEL"], ["LAYERCI"], ["HUDSON", "HUDSON_URL"], ["JENKINS", "JENKINS_URL"], ["MAGNUM"], ["NETLIFY"], ["NETLIFY", "NETLIFY_LOCAL", { ci: false }], ["NEVERCODE"], ["RENDER"], ["SAIL", "SAILCI"], ["SEMAPHORE"], ["SCREWDRIVER"], ["SHIPPABLE"], ["SOLANO", "TDDIUM"], ["STRIDER"], ["TEAMCITY", "TEAMCITY_VERSION"], ["TRAVIS"], ["VERCEL", "NOW_BUILDER"], ["VERCEL", "VERCEL", { ci: false }], ["VERCEL", "VERCEL_ENV", { ci: false }], ["APPCENTER", "APPCENTER_BUILD_ID"], ["CODESANDBOX", "CODESANDBOX_SSE", { ci: false }], ["CODESANDBOX", "CODESANDBOX_HOST", { ci: false }], ["STACKBLITZ"], ["STORMKIT"], ["CLEAVR"], ["ZEABUR"], ["CODESPHERE", "CODESPHERE_APP_ID", { ci: true }], ["RAILWAY", "RAILWAY_PROJECT_ID"], ["RAILWAY", "RAILWAY_SERVICE_ID"], ["DENO-DEPLOY", "DENO_DEPLOYMENT_ID"], ["FIREBASE_APP_HOSTING", "FIREBASE_APP_HOSTING", { ci: true }]];
9144
9148
  l = b();
9145
9149
  l.name;
@@ -11705,7 +11709,7 @@ var init_scan = __esm(() => {
11705
11709
  console.log();
11706
11710
  if (!isAuto) {
11707
11711
  const suggestion = filtered.map((h) => `stack add ${h.provider}`).join(`
11708
- `);
11712
+ `);
11709
11713
  console.log(colors2.dim(" Next steps:"));
11710
11714
  console.log(colors2.dim(` ${suggestion}
11711
11715
  `));
@@ -14515,6 +14519,8 @@ async function openFleetFile(path) {
14515
14519
  async function readFleetText(path, options = {}) {
14516
14520
  const { maxBytes = 16 * 1024 * 1024, tail = false, ioStatus = "invalid" } = options;
14517
14521
  try {
14522
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
14523
+ throw new Error("Invalid byte limit");
14518
14524
  const file = await openFleetFile(path);
14519
14525
  try {
14520
14526
  const stat = await file.stat();
@@ -14529,7 +14535,29 @@ async function readFleetText(path, options = {}) {
14529
14535
  `);
14530
14536
  text = newline < 0 ? "" : chunk.slice(newline + 1);
14531
14537
  } else {
14532
- text = await file.readFile("utf8");
14538
+ const initial = Math.min(64 * 1024, Math.max(1, stat.size), maxBytes);
14539
+ let buffer = Buffer.alloc(initial);
14540
+ let offset = 0;
14541
+ for (;; ) {
14542
+ if (offset === maxBytes) {
14543
+ const probe = Buffer.alloc(1);
14544
+ if ((await file.read(probe, 0, 1, offset)).bytesRead)
14545
+ throw new Error("Oversized source");
14546
+ break;
14547
+ }
14548
+ if (offset === buffer.length) {
14549
+ const grown = Buffer.alloc(Math.min(maxBytes, buffer.length * 2));
14550
+ buffer.copy(grown, 0, 0, offset);
14551
+ buffer = grown;
14552
+ }
14553
+ const { bytesRead } = await file.read(buffer, offset, buffer.length - offset, offset);
14554
+ if (!bytesRead)
14555
+ break;
14556
+ offset += bytesRead;
14557
+ }
14558
+ if (options.requireInitialSize && offset < stat.size)
14559
+ throw new Error("Incomplete source");
14560
+ text = buffer.subarray(0, offset).toString("utf8");
14533
14561
  }
14534
14562
  return { status: "ok", text };
14535
14563
  } finally {
@@ -15399,6 +15427,7 @@ async function collectFleetSilent(runs, now = Date.now()) {
15399
15427
  }
15400
15428
  const paths = [...authors, "dispatchd.log", "dispatch-events.log", "metrics/jobs.jsonl"];
15401
15429
  const inputs = await Promise.all(paths.map((path) => readFleetJsonl(join18(runs, path), {
15430
+ requireInitialSize: true,
15402
15431
  parseLine: parse3,
15403
15432
  malformedLines: "flag",
15404
15433
  flagRow: wellFormed
@@ -16074,7 +16103,10 @@ function serveFleet(paths, port) {
16074
16103
  hostname: "127.0.0.1",
16075
16104
  port,
16076
16105
  idleTimeout: 0,
16077
- fetch(request) {
16106
+ fetch(request, server) {
16107
+ const host = request.headers.get("host");
16108
+ if (host !== `127.0.0.1:${server.port}` && host !== `localhost:${server.port}`)
16109
+ return new Response("Forbidden", { status: 403 });
16078
16110
  if (new URL(request.url).pathname !== "/events")
16079
16111
  return new Response("Not found", { status: 404 });
16080
16112
  const abort = new AbortController;
@@ -18607,6 +18639,11 @@ var init_apply = __esm(() => {
18607
18639
  type: "boolean",
18608
18640
  default: false,
18609
18641
  description: "On partial failure, leave successfully-added services in .stack.toml instead of rolling them back."
18642
+ },
18643
+ dryRun: {
18644
+ type: "boolean",
18645
+ default: false,
18646
+ description: "Preview provisioning and wiring without making changes."
18610
18647
  }
18611
18648
  },
18612
18649
  async run({ args }) {
@@ -18614,6 +18651,23 @@ var init_apply = __esm(() => {
18614
18651
  const recipe = await pickRecipe(args.recipeId ? String(args.recipeId) : undefined);
18615
18652
  if (!recipe)
18616
18653
  return;
18654
+ if (args.dryRun) {
18655
+ console.log(` recipe ${recipe.id}`);
18656
+ await provisionProviders(recipe.providers, { rollback: false, dryRun: true });
18657
+ if (!args.noWire) {
18658
+ console.log(" Would check these secrets in Phantom and create applicable webhook stubs:");
18659
+ for (const { name } of recipe.providers) {
18660
+ const ref = findProviderRef(name);
18661
+ if (!ref)
18662
+ console.log(` ${name}: unknown provider`);
18663
+ else
18664
+ for (const secret of ref.secrets)
18665
+ console.log(` ${name}: ${secret}`);
18666
+ }
18667
+ }
18668
+ outro("Dry run complete — no changes made.");
18669
+ return;
18670
+ }
18617
18671
  if (!hasConfig()) {
18618
18672
  console.log(colors2.dim(" ○ no .stack.toml — auto-running `stack init --noInteractive`"));
18619
18673
  try {
@@ -18639,7 +18693,9 @@ var init_apply = __esm(() => {
18639
18693
  return;
18640
18694
  }
18641
18695
  }
18642
- await requirePhantom();
18696
+ if (recipe.providers.length > 0) {
18697
+ await requirePhantom();
18698
+ }
18643
18699
  console.log();
18644
18700
  console.log(` ${colors2.bold("recipe")} ${recipe.id}`);
18645
18701
  console.log(` ${colors2.dim("query")} ${recipe.query}`);
@@ -19161,7 +19217,7 @@ var init_completion = __esm(() => {
19161
19217
  ]
19162
19218
  },
19163
19219
  apply: {
19164
- flags: [{ name: "noWire" }, { name: "noRollback" }]
19220
+ flags: [{ name: "noWire" }, { name: "noRollback" }, { name: "dryRun" }]
19165
19221
  },
19166
19222
  swap: {
19167
19223
  completesProviders: true,
@@ -19262,7 +19318,7 @@ var package_default;
19262
19318
  var init_package = __esm(() => {
19263
19319
  package_default = {
19264
19320
  name: "@ashlr/stack",
19265
- version: "0.3.1",
19321
+ version: "0.3.7",
19266
19322
  description: "Ashlr Stack CLI — the control plane for your entire dev stack. One command to provision, wire, and operate every third-party service in your project. 39 providers, MCP-native.",
19267
19323
  license: "MIT",
19268
19324
  author: {
@@ -19310,13 +19366,13 @@ var init_package = __esm(() => {
19310
19366
  node: ">=20"
19311
19367
  },
19312
19368
  scripts: {
19313
- build: `bun build src/index.ts --outdir dist --target node --format esm && node -e "const fs=require('fs');const f='dist/index.js';let c=fs.readFileSync(f,'utf8');if(c.startsWith('#!'))c=c.slice(c.indexOf('\\n')+1);fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);fs.chmodSync(f,0o755)"`,
19369
+ build: `NODE_ENV=production bun build src/index.ts --outdir dist --target node --format esm && node -e "const fs=require('fs');const f='dist/index.js';let c=fs.readFileSync(f,'utf8');if(c.startsWith('#!'))c=c.slice(c.indexOf('\\n')+1);fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);fs.chmodSync(f,0o755)"`,
19314
19370
  prepack: "bun run build",
19315
19371
  dev: "bun run src/index.ts",
19316
19372
  typecheck: "bunx tsc --noEmit"
19317
19373
  },
19318
19374
  dependencies: {
19319
- "@ashlr/stack-core": "^0.3.1",
19375
+ "@ashlr/stack-core": "^0.3.7",
19320
19376
  "@clack/prompts": "^0.8.2",
19321
19377
  citty: "^0.1.6",
19322
19378
  picocolors: "^1.1.1"
@@ -19802,7 +19858,7 @@ async function checkForUpdate(currentVersion, _cacheFile) {
19802
19858
  }
19803
19859
 
19804
19860
  // src/index.ts
19805
- var VERSION = "0.3.1";
19861
+ var VERSION = "0.3.7";
19806
19862
  var main = defineCommand({
19807
19863
  meta: {
19808
19864
  name: "stack",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashlr/stack",
3
- "version": "0.3.1",
3
+ "version": "0.3.7",
4
4
  "description": "Ashlr Stack CLI — the control plane for your entire dev stack. One command to provision, wire, and operate every third-party service in your project. 39 providers, MCP-native.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -48,13 +48,13 @@
48
48
  "node": ">=20"
49
49
  },
50
50
  "scripts": {
51
- "build": "bun build src/index.ts --outdir dist --target node --format esm && node -e \"const fs=require('fs');const f='dist/index.js';let c=fs.readFileSync(f,'utf8');if(c.startsWith('#!'))c=c.slice(c.indexOf('\\n')+1);fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);fs.chmodSync(f,0o755)\"",
51
+ "build": "NODE_ENV=production bun build src/index.ts --outdir dist --target node --format esm && node -e \"const fs=require('fs');const f='dist/index.js';let c=fs.readFileSync(f,'utf8');if(c.startsWith('#!'))c=c.slice(c.indexOf('\\n')+1);fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);fs.chmodSync(f,0o755)\"",
52
52
  "prepack": "bun run build",
53
53
  "dev": "bun run src/index.ts",
54
54
  "typecheck": "bunx tsc --noEmit"
55
55
  },
56
56
  "dependencies": {
57
- "@ashlr/stack-core": "^0.3.1",
57
+ "@ashlr/stack-core": "^0.3.7",
58
58
  "@clack/prompts": "^0.8.2",
59
59
  "citty": "^0.1.6",
60
60
  "picocolors": "^1.1.1"