saasfunnels 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/library.js +3 -3
- package/dist/saasfunnels.js +76 -13
- package/dist/types/identity.d.ts +2 -2
- package/dist/types/mcp.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ approved set is reviewable.
|
|
|
48
48
|
### GitHub Action
|
|
49
49
|
|
|
50
50
|
```yaml
|
|
51
|
-
- uses: LeadEngine-ai/saasfunnels-cli/action@v0.2
|
|
51
|
+
- uses: LeadEngine-ai/saasfunnels-cli/action@v0.5.2
|
|
52
52
|
with:
|
|
53
53
|
api-key: ${{ secrets.SAASFUNNELS_API_KEY }}
|
|
54
54
|
cli-version: 0.2.1 # pin it; a floating version rebaselines drift
|
|
@@ -92,9 +92,9 @@ npm run release:verify
|
|
|
92
92
|
|
|
93
93
|
The package allowlist is enforced as `LICENSE`, `README.md`, `package.json`, the `saasfunnels` executable bundle, and the typed server-side library bundle/declarations.
|
|
94
94
|
|
|
95
|
-
Documentation: https://docs.saasfunnels.
|
|
95
|
+
Documentation: https://docs.saasfunnels.com/developer-tools/saasfunnels-cli
|
|
96
96
|
|
|
97
|
-
Support: support@saasfunnels.
|
|
97
|
+
Support: support@saasfunnels.com
|
|
98
98
|
|
|
99
99
|
### Guided application discovery
|
|
100
100
|
|
|
@@ -114,6 +114,8 @@ CLI 0.5.0 requires Setup contract 4 and stops before creating a run against an o
|
|
|
114
114
|
|
|
115
115
|
Discovery follows Git ignore rules (including custom build folders) while keeping tracked application sources and new, non-ignored source files. Standalone folders without Git use the standard source filters.
|
|
116
116
|
|
|
117
|
+
The coding-agent installation handoff can also create `.saasfunnels/shared-installation.json` with the shared host's source binding, supported entry families, and `entryBindings` naming each installed application event, API invocation, or lifecycle entry by its exact `kind` and `key`. Setup submits that binding once using the existing workspace, integration, environment, and installation revision. It records progress after each accepted stage. If the file is absent, existing workspace installation evidence is preserved; creating another Funnel never requires this file or another catalog upload. The application must still verify the installed host and hooks at runtime before Setup marks them connected.
|
|
118
|
+
|
|
117
119
|
The command does not implement arbitrary payment code, approve feature access, or verify transactions. Complete **Review and verify** in Setup after deploying the runtime and payment integration. Requires the server's guided setup API release.
|
|
118
120
|
|
|
119
121
|
### Discovery accuracy and review
|
package/dist/library.js
CHANGED
|
@@ -233553,7 +233553,7 @@ You are adding SaaSFunnels Direct API events to this customer application. Treat
|
|
|
233553
233553
|
- Endpoint: \`${endpoint}\`
|
|
233554
233554
|
- Auth header: \`Authorization: Bearer ${ingestKeyPlaceholder}\`
|
|
233555
233555
|
- Environment variable: \`SAASFUNNELS_INGEST_API_KEY=${ingestKeyPlaceholder}\`
|
|
233556
|
-
- Help article: \`https://docs.saasfunnels.
|
|
233556
|
+
- Help article: \`https://docs.saasfunnels.com/integrations-and-sources/direct-api-installation\`
|
|
233557
233557
|
|
|
233558
233558
|
## Direct API-first event discovery
|
|
233559
233559
|
|
|
@@ -235858,9 +235858,9 @@ function validateFeatureCatalogManifest(value) {
|
|
|
235858
235858
|
|
|
235859
235859
|
// src/identity.ts
|
|
235860
235860
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
235861
|
-
var SAASFUNNELS_CLI_VERSION = "0.5.
|
|
235861
|
+
var SAASFUNNELS_CLI_VERSION = "0.5.2";
|
|
235862
235862
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
235863
|
-
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.
|
|
235863
|
+
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.com";
|
|
235864
235864
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
235865
235865
|
apiBaseUrl: ["SAASFUNNELS_API_BASE_URL", "PREVENUE_API_BASE_URL"],
|
|
235866
235866
|
apiKey: ["SAASFUNNELS_API_KEY", "PREVENUE_API_KEY"],
|
package/dist/saasfunnels.js
CHANGED
|
@@ -218902,6 +218902,10 @@ var require_dist = __commonJS({
|
|
|
218902
218902
|
// src/saasfunnels.ts
|
|
218903
218903
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
218904
218904
|
|
|
218905
|
+
// src/cli.ts
|
|
218906
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
218907
|
+
import { promisify as promisify5 } from "node:util";
|
|
218908
|
+
|
|
218905
218909
|
// src/setup-access.ts
|
|
218906
218910
|
import { execFile } from "node:child_process";
|
|
218907
218911
|
import { readFile } from "node:fs/promises";
|
|
@@ -234044,9 +234048,9 @@ function extractSetupPatch(source, catalog, baseline) {
|
|
|
234044
234048
|
|
|
234045
234049
|
// src/identity.ts
|
|
234046
234050
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
234047
|
-
var SAASFUNNELS_CLI_VERSION = "0.5.
|
|
234051
|
+
var SAASFUNNELS_CLI_VERSION = "0.5.2";
|
|
234048
234052
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
234049
|
-
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.
|
|
234053
|
+
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.com";
|
|
234050
234054
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
234051
234055
|
apiBaseUrl: ["SAASFUNNELS_API_BASE_URL", "PREVENUE_API_BASE_URL"],
|
|
234052
234056
|
apiKey: ["SAASFUNNELS_API_KEY", "PREVENUE_API_KEY"],
|
|
@@ -234930,7 +234934,7 @@ You are adding SaaSFunnels Direct API events to this customer application. Treat
|
|
|
234930
234934
|
- Endpoint: \`${endpoint}\`
|
|
234931
234935
|
- Auth header: \`Authorization: Bearer ${ingestKeyPlaceholder}\`
|
|
234932
234936
|
- Environment variable: \`SAASFUNNELS_INGEST_API_KEY=${ingestKeyPlaceholder}\`
|
|
234933
|
-
- Help article: \`https://docs.saasfunnels.
|
|
234937
|
+
- Help article: \`https://docs.saasfunnels.com/integrations-and-sources/direct-api-installation\`
|
|
234934
234938
|
|
|
234935
234939
|
## Direct API-first event discovery
|
|
234936
234940
|
|
|
@@ -238214,6 +238218,7 @@ var contextSchema = external_exports.object({
|
|
|
238214
238218
|
installationId: external_exports.string().nullable(),
|
|
238215
238219
|
integrationId: external_exports.string().nullable(),
|
|
238216
238220
|
environment: external_exports.enum(["test", "production"]),
|
|
238221
|
+
sharedInstallation: external_exports.object({ registryRevision: external_exports.number().int().nonnegative().nullable(), fingerprint: external_exports.string().nullable(), verified: external_exports.boolean() }).optional(),
|
|
238217
238222
|
catalogReady: external_exports.boolean(),
|
|
238218
238223
|
planNames: external_exports.array(external_exports.string()),
|
|
238219
238224
|
catalogPrices: external_exports.array(
|
|
@@ -238386,7 +238391,7 @@ async function runGuidedSetup(options) {
|
|
|
238386
238391
|
})).run;
|
|
238387
238392
|
if (!run) throw new Error("Setup did not return a run.");
|
|
238388
238393
|
await mkdir3(join6(options.cwd, ".saasfunnels"), { recursive: true });
|
|
238389
|
-
|
|
238394
|
+
const saveRun = () => writeFile3(
|
|
238390
238395
|
statePath,
|
|
238391
238396
|
JSON.stringify(
|
|
238392
238397
|
{
|
|
@@ -238394,13 +238399,32 @@ async function runGuidedSetup(options) {
|
|
|
238394
238399
|
generation: context.generation,
|
|
238395
238400
|
repositoryKey,
|
|
238396
238401
|
revision,
|
|
238397
|
-
runId: run.id
|
|
238402
|
+
runId: run.id,
|
|
238403
|
+
receipts: run.receipts
|
|
238398
238404
|
},
|
|
238399
238405
|
null,
|
|
238400
238406
|
2
|
|
238401
238407
|
) + "\n",
|
|
238402
238408
|
{ mode: 384 }
|
|
238403
238409
|
);
|
|
238410
|
+
await saveRun();
|
|
238411
|
+
try {
|
|
238412
|
+
const shared = JSON.parse(await readFile6(join6(options.cwd, ".saasfunnels", "shared-installation.json"), "utf8"));
|
|
238413
|
+
if (context.sharedInstallation?.registryRevision == null)
|
|
238414
|
+
throw new Error("The app connection has not issued a verified identity yet. Complete its shared installation first.");
|
|
238415
|
+
await request("/api/developer-tools/setup/installation", {
|
|
238416
|
+
...shared,
|
|
238417
|
+
schemaVersion: 1,
|
|
238418
|
+
installationId: context.installationId,
|
|
238419
|
+
installationRevision: context.sharedInstallation.registryRevision,
|
|
238420
|
+
integrationId: context.integrationId,
|
|
238421
|
+
environment: context.environment,
|
|
238422
|
+
repositoryKey,
|
|
238423
|
+
repositoryRevision: head
|
|
238424
|
+
});
|
|
238425
|
+
} catch (error51) {
|
|
238426
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
238427
|
+
}
|
|
238404
238428
|
const update = (state, failureCode = null) => {
|
|
238405
238429
|
progressState = state;
|
|
238406
238430
|
progressFailure = failureCode;
|
|
@@ -238446,6 +238470,7 @@ async function runGuidedSetup(options) {
|
|
|
238446
238470
|
if (typeof result3.sequence === "number")
|
|
238447
238471
|
run.sequence = result3.sequence;
|
|
238448
238472
|
run.receipts[currentStage] = result3.receipt ?? { complete: true };
|
|
238473
|
+
await saveRun();
|
|
238449
238474
|
if (currentStage === "plans")
|
|
238450
238475
|
run.planNames = result3.planNames ?? evidence.plans.flatMap((plan) => [plan.key, plan.name]);
|
|
238451
238476
|
});
|
|
@@ -238708,7 +238733,7 @@ async function runGuidedSetup(options) {
|
|
|
238708
238733
|
}
|
|
238709
238734
|
return {
|
|
238710
238735
|
exitCode: 0,
|
|
238711
|
-
stdout: `Discovery submitted. ${selection === "rejected" ? "This submission was rejected in Setup. Current findings and decisions are retained." : selection === "superseded" ? "A newer submission is selected in Setup. Current findings and decisions are retained." : selection === "complete_candidate" ? "Existing findings and decisions are retained; review proposed removals or finish the pending publication in Setup." : "Corrections are ready for review; unrelated records and saved decisions are retained."} Open https://app.saasfunnels.
|
|
238736
|
+
stdout: `Discovery submitted. ${selection === "rejected" ? "This submission was rejected in Setup. Current findings and decisions are retained." : selection === "superseded" ? "A newer submission is selected in Setup. Current findings and decisions are retained." : selection === "complete_candidate" ? "Existing findings and decisions are retained; review proposed removals or finish the pending publication in Setup." : "Corrections are ready for review; unrelated records and saved decisions are retained."} Open https://app.saasfunnels.com/app/setup. Configuration has not been published.
|
|
238712
238737
|
`,
|
|
238713
238738
|
stderr: ""
|
|
238714
238739
|
};
|
|
@@ -240767,7 +240792,14 @@ async function commandFeatures(args, flags, options) {
|
|
|
240767
240792
|
}
|
|
240768
240793
|
if (args[0] === "handoff") {
|
|
240769
240794
|
const path2 = flagString(flags, "file") ?? ".saasfunnels/catalog.yaml";
|
|
240770
|
-
const
|
|
240795
|
+
const gitValue = async (...args2) => {
|
|
240796
|
+
try {
|
|
240797
|
+
return (await promisify5(execFile5)("git", args2, { cwd: cwd(options), timeout: 5e3, maxBuffer: 8192 })).stdout.trim();
|
|
240798
|
+
} catch {
|
|
240799
|
+
return "";
|
|
240800
|
+
}
|
|
240801
|
+
};
|
|
240802
|
+
const repositoryRevision = flagString(flags, "repository-revision") ?? await gitValue("rev-parse", "HEAD");
|
|
240771
240803
|
if (!repositoryRevision) {
|
|
240772
240804
|
return result(
|
|
240773
240805
|
2,
|
|
@@ -240775,7 +240807,16 @@ async function commandFeatures(args, flags, options) {
|
|
|
240775
240807
|
"Missing --repository-revision for the structured Feature handoff.\n"
|
|
240776
240808
|
);
|
|
240777
240809
|
}
|
|
240778
|
-
|
|
240810
|
+
let repositoryKey = flagString(flags, "repository-key");
|
|
240811
|
+
if (!repositoryKey) {
|
|
240812
|
+
const remote = await gitValue("remote", "get-url", "origin");
|
|
240813
|
+
if (remote) {
|
|
240814
|
+
try {
|
|
240815
|
+
repositoryKey = safeRepositoryKey(remote);
|
|
240816
|
+
} catch {
|
|
240817
|
+
}
|
|
240818
|
+
}
|
|
240819
|
+
}
|
|
240779
240820
|
if (!repositoryKey) {
|
|
240780
240821
|
return result(
|
|
240781
240822
|
2,
|
|
@@ -240796,10 +240837,32 @@ async function commandFeatures(args, flags, options) {
|
|
|
240796
240837
|
);
|
|
240797
240838
|
}
|
|
240798
240839
|
const discoveryRoots = (flagString(flags, "discovery-roots") ?? "").split(",").map((root) => root.trim()).filter(Boolean);
|
|
240799
|
-
|
|
240800
|
-
|
|
240801
|
-
"utf8"
|
|
240802
|
-
)
|
|
240840
|
+
let source;
|
|
240841
|
+
try {
|
|
240842
|
+
source = await filesystem(options).readFile(resolve5(cwd(options), path2), "utf8");
|
|
240843
|
+
} catch (error51) {
|
|
240844
|
+
if (error51.code !== "ENOENT") throw error51;
|
|
240845
|
+
const key4 = envValue2(options, SAASFUNNELS_ENV.apiKey);
|
|
240846
|
+
if (key4 && !flagString(flags, "file")) {
|
|
240847
|
+
const query = new URLSearchParams({ repositoryKey, repositoryRevision, environment: flagString(flags, "environment") ?? "production", ...flagString(flags, "discovery-roots") ? { discoveryRoots: JSON.stringify([...discoveryRoots].sort()) } : {} });
|
|
240848
|
+
try {
|
|
240849
|
+
const response = await (options.fetch ?? fetch)(`${apiBaseUrl2(options, flags)}/api/developer-tools/features/instrumentation?${query}`, { headers: { authorization: `Bearer ${key4}` }, signal: AbortSignal.timeout(15e3) });
|
|
240850
|
+
if (response.ok) {
|
|
240851
|
+
const evidence = await response.json();
|
|
240852
|
+
if (evidence.reused === true) return jsonMode(flags) ? jsonResult(0, { reused: true, currentRevision: evidence.currentRevision === true, manifestFingerprint: evidence.manifestFingerprint, validatedAt: evidence.validatedAt }) : result(
|
|
240853
|
+
0,
|
|
240854
|
+
`Reused the existing workspace Feature evidence. No catalog was uploaded.${evidence.currentRevision ? "" : " This is the previous scan, not verification of this checkout; Setup shows any remaining review."} Open Setup to continue.
|
|
240855
|
+
`
|
|
240856
|
+
);
|
|
240857
|
+
} else if (response.status !== 404) {
|
|
240858
|
+
return result(2, "", "Workspace Feature evidence could not be checked. Open Setup to check the connection and resume; your existing catalog has not changed.\n");
|
|
240859
|
+
}
|
|
240860
|
+
} catch {
|
|
240861
|
+
return result(2, "", "Workspace Feature evidence could not be checked. Open Setup to check the connection and resume; your existing catalog has not changed.\n");
|
|
240862
|
+
}
|
|
240863
|
+
}
|
|
240864
|
+
return result(2, "", "No local discovery file was found. Open Setup to reuse your workspace catalog and finish any missing connection steps. Do not recreate the catalog.\n");
|
|
240865
|
+
}
|
|
240803
240866
|
const validation = parseFeatureManifestSource(source);
|
|
240804
240867
|
if (!validation.ok || !validation.manifest) {
|
|
240805
240868
|
return jsonMode(flags) ? jsonResult(1, validation) : result(
|
|
@@ -241478,7 +241541,7 @@ async function runSaaSFunnelsCli(argv2, options = {}) {
|
|
|
241478
241541
|
return result(0, usage());
|
|
241479
241542
|
if (command === "init") return await commandInit(parsed.flags, options);
|
|
241480
241543
|
if (command === "setup") {
|
|
241481
|
-
const help = "SaaSFunnels setup\n\nRun npx --yes saasfunnels@latest setup in your app repository.\nIt checks access, asks before sending findings, and discovers features and plans for review.\nUse --resume after an interruption, or --env-file .env.local to load developer access.\nFor automation, supply SAASFUNNELS_API_KEY and explicit upload approval with --send --non-interactive.\nApp implementation, deployment and payment verification remain in https://app.saasfunnels.
|
|
241544
|
+
const help = "SaaSFunnels setup\n\nRun npx --yes saasfunnels@latest setup in your app repository.\nIt checks access, asks before sending findings, and discovers features and plans for review.\nUse --resume after an interruption, or --env-file .env.local to load developer access.\nFor automation, supply SAASFUNNELS_API_KEY and explicit upload approval with --send --non-interactive.\nApp implementation, deployment and payment verification remain in https://app.saasfunnels.com/app/setup.\n";
|
|
241482
241545
|
if (hasFlag(parsed.flags, "help") || args[0] === "help") return result(0, help);
|
|
241483
241546
|
if (args.length > 1 || args[0] && args[0] !== "run") return result(2, "", help);
|
|
241484
241547
|
const interactive = !jsonMode(parsed.flags) && !hasFlag(parsed.flags, "non-interactive");
|
package/dist/types/identity.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
2
|
-
export declare const SAASFUNNELS_CLI_VERSION = "0.5.
|
|
2
|
+
export declare const SAASFUNNELS_CLI_VERSION = "0.5.2";
|
|
3
3
|
export declare const SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
4
|
-
export declare const SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.
|
|
4
|
+
export declare const SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.com";
|
|
5
5
|
export declare const SAASFUNNELS_ENV: Readonly<{
|
|
6
6
|
apiBaseUrl: string[];
|
|
7
7
|
apiKey: string[];
|
package/dist/types/mcp.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type SaaSFunnelsMcpOptions = {
|
|
|
77
77
|
output?: Pick<NodeJS.WritableStream, "write">;
|
|
78
78
|
};
|
|
79
79
|
declare const mcpProtocolVersion = "2025-06-18";
|
|
80
|
-
declare const mcpServerVersion = "0.5.
|
|
80
|
+
declare const mcpServerVersion = "0.5.2";
|
|
81
81
|
export declare function saasFunnelsMcpResources(): McpResourceDefinition[];
|
|
82
82
|
export declare function readSaaSFunnelsMcpResource(uri: string, options?: SaaSFunnelsMcpOptions): string;
|
|
83
83
|
export declare function saasFunnelsMcpToolDefinitions({ allowFunnelTests, allowFunnelWrites, allowTestWrites, includeHostedAccountTools, }?: {
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saasfunnels",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "SaaSFunnels CLI and local stdio MCP server",
|
|
6
6
|
"author": "SaaSFunnels",
|
|
7
|
-
"homepage": "https://docs.saasfunnels.
|
|
7
|
+
"homepage": "https://docs.saasfunnels.com/developer-tools/saasfunnels-cli",
|
|
8
8
|
"bugs": {
|
|
9
|
-
"email": "support@saasfunnels.
|
|
9
|
+
"email": "support@saasfunnels.com",
|
|
10
10
|
"url": "https://github.com/LeadEngine-ai/saasfunnels-cli/issues"
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|