@ashlr/stack 0.3.0 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/index.js +70 -17
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @ashlr/stack
|
|
2
2
|
|
|
3
|
-
> The `stack` CLI — configure supported services with Phantom-backed credentials and MCP wiring.
|
|
3
|
+
> The `stack` CLI — configure supported services with Phantom-backed credentials and MCP wiring. Use `stack providers` to inspect your installed catalog.
|
|
4
4
|
|
|
5
5
|
Part of [Ashlr Stack](https://stack.ashlr.ai). See the monorepo at [ashlrai/ashlr-stack](https://github.com/ashlrai/ashlr-stack).
|
|
6
6
|
|
|
7
7
|
## What it is
|
|
8
8
|
|
|
9
|
-
`stack`
|
|
9
|
+
`stack` offers template-assisted setup for selected supported services. For example:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
stack init --template nextjs-supabase-posthog
|
|
@@ -31,17 +31,17 @@ irm https://stack.ashlr.ai/install.ps1 | iex
|
|
|
31
31
|
Or install manually from the registry:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
bun add -g @ashlr/stack ashlr-stack-mcp # preferred
|
|
35
|
-
# or
|
|
36
34
|
npm i -g @ashlr/stack ashlr-stack-mcp
|
|
35
|
+
# or use Bun as the package manager
|
|
36
|
+
bun add -g @ashlr/stack ashlr-stack-mcp
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Prerequisites:
|
|
40
40
|
|
|
41
|
-
-
|
|
41
|
+
- Node.js >= 20 for this npm package, including with `npx`; check your installed package manifest. Source development uses [Bun](https://bun.sh).
|
|
42
42
|
- [Phantom Secrets](https://phm.dev/secrets) — installed and initialized for the intended project before writing setup flows
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Verify your installed `stack --version` and command `--help`; available commands depend on the installed version. Package installation does not establish provider acceptance.
|
|
45
45
|
|
|
46
46
|
## Quickstart
|
|
47
47
|
|
|
@@ -72,7 +72,7 @@ stack exec -- bun dev
|
|
|
72
72
|
|
|
73
73
|
## Commands
|
|
74
74
|
|
|
75
|
-
A
|
|
75
|
+
A guide to common commands; check your installed version's help for availability:
|
|
76
76
|
|
|
77
77
|
| Command | What it does |
|
|
78
78
|
| --- | --- |
|
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.
|
|
1494
|
-
Object.freeze({ id: "cli", name: "@ashlr/stack", version: "0.3.
|
|
1495
|
-
Object.freeze({ id: "mcp", name: "ashlr-stack-mcp", version: "0.3.
|
|
1493
|
+
Object.freeze({ id: "core", name: "@ashlr/stack-core", version: "0.3.6", distribution: "npm" }),
|
|
1494
|
+
Object.freeze({ id: "cli", name: "@ashlr/stack", version: "0.3.6", distribution: "npm" }),
|
|
1495
|
+
Object.freeze({ id: "mcp", name: "ashlr-stack-mcp", version: "0.3.6", 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 =
|
|
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
|
-
|
|
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 && "
|
|
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,27 @@ async function readFleetText(path, options = {}) {
|
|
|
14529
14535
|
`);
|
|
14530
14536
|
text = newline < 0 ? "" : chunk.slice(newline + 1);
|
|
14531
14537
|
} else {
|
|
14532
|
-
|
|
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
|
+
text = buffer.subarray(0, offset).toString("utf8");
|
|
14533
14559
|
}
|
|
14534
14560
|
return { status: "ok", text };
|
|
14535
14561
|
} finally {
|
|
@@ -16074,7 +16100,10 @@ function serveFleet(paths, port) {
|
|
|
16074
16100
|
hostname: "127.0.0.1",
|
|
16075
16101
|
port,
|
|
16076
16102
|
idleTimeout: 0,
|
|
16077
|
-
fetch(request) {
|
|
16103
|
+
fetch(request, server) {
|
|
16104
|
+
const host = request.headers.get("host");
|
|
16105
|
+
if (host !== `127.0.0.1:${server.port}` && host !== `localhost:${server.port}`)
|
|
16106
|
+
return new Response("Forbidden", { status: 403 });
|
|
16078
16107
|
if (new URL(request.url).pathname !== "/events")
|
|
16079
16108
|
return new Response("Not found", { status: 404 });
|
|
16080
16109
|
const abort = new AbortController;
|
|
@@ -18607,6 +18636,11 @@ var init_apply = __esm(() => {
|
|
|
18607
18636
|
type: "boolean",
|
|
18608
18637
|
default: false,
|
|
18609
18638
|
description: "On partial failure, leave successfully-added services in .stack.toml instead of rolling them back."
|
|
18639
|
+
},
|
|
18640
|
+
dryRun: {
|
|
18641
|
+
type: "boolean",
|
|
18642
|
+
default: false,
|
|
18643
|
+
description: "Preview provisioning and wiring without making changes."
|
|
18610
18644
|
}
|
|
18611
18645
|
},
|
|
18612
18646
|
async run({ args }) {
|
|
@@ -18614,6 +18648,23 @@ var init_apply = __esm(() => {
|
|
|
18614
18648
|
const recipe = await pickRecipe(args.recipeId ? String(args.recipeId) : undefined);
|
|
18615
18649
|
if (!recipe)
|
|
18616
18650
|
return;
|
|
18651
|
+
if (args.dryRun) {
|
|
18652
|
+
console.log(` recipe ${recipe.id}`);
|
|
18653
|
+
await provisionProviders(recipe.providers, { rollback: false, dryRun: true });
|
|
18654
|
+
if (!args.noWire) {
|
|
18655
|
+
console.log(" Would check these secrets in Phantom and create applicable webhook stubs:");
|
|
18656
|
+
for (const { name } of recipe.providers) {
|
|
18657
|
+
const ref = findProviderRef(name);
|
|
18658
|
+
if (!ref)
|
|
18659
|
+
console.log(` ${name}: unknown provider`);
|
|
18660
|
+
else
|
|
18661
|
+
for (const secret of ref.secrets)
|
|
18662
|
+
console.log(` ${name}: ${secret}`);
|
|
18663
|
+
}
|
|
18664
|
+
}
|
|
18665
|
+
outro("Dry run complete — no changes made.");
|
|
18666
|
+
return;
|
|
18667
|
+
}
|
|
18617
18668
|
if (!hasConfig()) {
|
|
18618
18669
|
console.log(colors2.dim(" ○ no .stack.toml — auto-running `stack init --noInteractive`"));
|
|
18619
18670
|
try {
|
|
@@ -18639,7 +18690,9 @@ var init_apply = __esm(() => {
|
|
|
18639
18690
|
return;
|
|
18640
18691
|
}
|
|
18641
18692
|
}
|
|
18642
|
-
|
|
18693
|
+
if (recipe.providers.length > 0) {
|
|
18694
|
+
await requirePhantom();
|
|
18695
|
+
}
|
|
18643
18696
|
console.log();
|
|
18644
18697
|
console.log(` ${colors2.bold("recipe")} ${recipe.id}`);
|
|
18645
18698
|
console.log(` ${colors2.dim("query")} ${recipe.query}`);
|
|
@@ -19161,7 +19214,7 @@ var init_completion = __esm(() => {
|
|
|
19161
19214
|
]
|
|
19162
19215
|
},
|
|
19163
19216
|
apply: {
|
|
19164
|
-
flags: [{ name: "noWire" }, { name: "noRollback" }]
|
|
19217
|
+
flags: [{ name: "noWire" }, { name: "noRollback" }, { name: "dryRun" }]
|
|
19165
19218
|
},
|
|
19166
19219
|
swap: {
|
|
19167
19220
|
completesProviders: true,
|
|
@@ -19262,7 +19315,7 @@ var package_default;
|
|
|
19262
19315
|
var init_package = __esm(() => {
|
|
19263
19316
|
package_default = {
|
|
19264
19317
|
name: "@ashlr/stack",
|
|
19265
|
-
version: "0.3.
|
|
19318
|
+
version: "0.3.6",
|
|
19266
19319
|
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
19320
|
license: "MIT",
|
|
19268
19321
|
author: {
|
|
@@ -19310,13 +19363,13 @@ var init_package = __esm(() => {
|
|
|
19310
19363
|
node: ">=20"
|
|
19311
19364
|
},
|
|
19312
19365
|
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)"`,
|
|
19366
|
+
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
19367
|
prepack: "bun run build",
|
|
19315
19368
|
dev: "bun run src/index.ts",
|
|
19316
19369
|
typecheck: "bunx tsc --noEmit"
|
|
19317
19370
|
},
|
|
19318
19371
|
dependencies: {
|
|
19319
|
-
"@ashlr/stack-core": "^0.3.
|
|
19372
|
+
"@ashlr/stack-core": "^0.3.6",
|
|
19320
19373
|
"@clack/prompts": "^0.8.2",
|
|
19321
19374
|
citty: "^0.1.6",
|
|
19322
19375
|
picocolors: "^1.1.1"
|
|
@@ -19802,7 +19855,7 @@ async function checkForUpdate(currentVersion, _cacheFile) {
|
|
|
19802
19855
|
}
|
|
19803
19856
|
|
|
19804
19857
|
// src/index.ts
|
|
19805
|
-
var VERSION = "0.3.
|
|
19858
|
+
var VERSION = "0.3.6";
|
|
19806
19859
|
var main = defineCommand({
|
|
19807
19860
|
meta: {
|
|
19808
19861
|
name: "stack",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ashlr/stack",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
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.
|
|
57
|
+
"@ashlr/stack-core": "^0.3.6",
|
|
58
58
|
"@clack/prompts": "^0.8.2",
|
|
59
59
|
"citty": "^0.1.6",
|
|
60
60
|
"picocolors": "^1.1.1"
|