@agent-native/recap-cli 0.0.0-beta-20260820202238
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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -0
- package/dist/copy-recap-workflow.d.ts +2 -0
- package/dist/copy-recap-workflow.d.ts.map +1 -0
- package/dist/copy-recap-workflow.js +9 -0
- package/dist/copy-recap-workflow.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/openai-compatible-endpoint.d.ts +3 -0
- package/dist/openai-compatible-endpoint.d.ts.map +1 -0
- package/dist/openai-compatible-endpoint.js +23 -0
- package/dist/openai-compatible-endpoint.js.map +1 -0
- package/dist/plan-blocks.d.ts +19 -0
- package/dist/plan-blocks.d.ts.map +1 -0
- package/dist/plan-blocks.js +96 -0
- package/dist/plan-blocks.js.map +1 -0
- package/dist/plan-publish-store.d.ts +62 -0
- package/dist/plan-publish-store.d.ts.map +1 -0
- package/dist/plan-publish-store.js +128 -0
- package/dist/plan-publish-store.js.map +1 -0
- package/dist/pr-visual-recap-workflow.d.ts +3 -0
- package/dist/pr-visual-recap-workflow.d.ts.map +1 -0
- package/dist/pr-visual-recap-workflow.js +10 -0
- package/dist/pr-visual-recap-workflow.js.map +1 -0
- package/dist/recap.d.ts +575 -0
- package/dist/recap.d.ts.map +1 -0
- package/dist/recap.js +4029 -0
- package/dist/recap.js.map +1 -0
- package/dist/skill-content/connection.d.ts +2 -0
- package/dist/skill-content/connection.d.ts.map +1 -0
- package/dist/skill-content/connection.js +53 -0
- package/dist/skill-content/connection.js.map +1 -0
- package/dist/skill-content/local-files.d.ts +2 -0
- package/dist/skill-content/local-files.d.ts.map +1 -0
- package/dist/skill-content/local-files.js +101 -0
- package/dist/skill-content/local-files.js.map +1 -0
- package/dist/skill-content/visual-recap-skill.d.ts +2 -0
- package/dist/skill-content/visual-recap-skill.d.ts.map +1 -0
- package/dist/skill-content/visual-recap-skill.js +554 -0
- package/dist/skill-content/visual-recap-skill.js.map +1 -0
- package/dist/skill-content/wireframe.d.ts +4 -0
- package/dist/skill-content/wireframe.d.ts.map +1 -0
- package/dist/skill-content/wireframe.js +347 -0
- package/dist/skill-content/wireframe.js.map +1 -0
- package/dist/skill-content.d.ts +9 -0
- package/dist/skill-content.d.ts.map +1 -0
- package/dist/skill-content.js +11 -0
- package/dist/skill-content.js.map +1 -0
- package/dist/workflows/pr-visual-recap.yml +1064 -0
- package/package.json +48 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runRecap } from "./recap.js";
|
|
3
|
+
const argv = process.argv.slice(2);
|
|
4
|
+
if (argv[0] === "recap")
|
|
5
|
+
argv.shift();
|
|
6
|
+
runRecap(argv).catch((error) => {
|
|
7
|
+
console.error(error instanceof Error ? error.message : error);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO;IAAE,IAAI,CAAC,KAAK,EAAE,CAAC;AAEtC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\n\nimport { runRecap } from \"./recap.js\";\n\nconst argv = process.argv.slice(2);\nif (argv[0] === \"recap\") argv.shift();\n\nrunRecap(argv).catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-recap-workflow.d.ts","sourceRoot":"","sources":["../src/copy-recap-workflow.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { copyFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const distDir = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const source = path.resolve(distDir, "../../../.github/workflows/pr-visual-recap.yml");
|
|
6
|
+
const destination = path.join(distDir, "workflows", "pr-visual-recap.yml");
|
|
7
|
+
mkdirSync(path.dirname(destination), { recursive: true });
|
|
8
|
+
copyFileSync(source, destination);
|
|
9
|
+
//# sourceMappingURL=copy-recap-workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-recap-workflow.js","sourceRoot":"","sources":["../src/copy-recap-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CACzB,OAAO,EACP,gDAAgD,CACjD,CAAC;AACF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,qBAAqB,CAAC,CAAC;AAE3E,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC","sourcesContent":["import { copyFileSync, mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst distDir = path.dirname(fileURLToPath(import.meta.url));\nconst source = path.resolve(\n distDir,\n \"../../../.github/workflows/pr-visual-recap.yml\",\n);\nconst destination = path.join(distDir, \"workflows\", \"pr-visual-recap.yml\");\n\nmkdirSync(path.dirname(destination), { recursive: true });\ncopyFileSync(source, destination);\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./recap.js";
|
|
2
|
+
export { DEFAULT_PLAN_APP_URL, defaultPlanBlocksOut, fetchPlanBlockCatalog, normalizePlanBlockFormat, planActionEndpoint, } from "./plan-blocks.js";
|
|
3
|
+
export type { FetchPlanBlockCatalogInput, FetchPlanBlockCatalogResult, PlanBlockFormat, } from "./plan-blocks.js";
|
|
4
|
+
export { isFirstPartyPlanHost, planPublishConfigPath, readPlanPublishAuth, writePlanPublishAuth, } from "./plan-publish-store.js";
|
|
5
|
+
export { PR_VISUAL_RECAP_WORKFLOW_YML } from "./pr-visual-recap-workflow.js";
|
|
6
|
+
export { CONNECTION_REFERENCE_MD, RECAP_REFERENCE_FILES, VISUAL_RECAP_SKILL_MD, } from "./skill-content.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./recap.js";
|
|
2
|
+
export { DEFAULT_PLAN_APP_URL, defaultPlanBlocksOut, fetchPlanBlockCatalog, normalizePlanBlockFormat, planActionEndpoint, } from "./plan-blocks.js";
|
|
3
|
+
export { isFirstPartyPlanHost, planPublishConfigPath, readPlanPublishAuth, writePlanPublishAuth, } from "./plan-publish-store.js";
|
|
4
|
+
export { PR_VISUAL_RECAP_WORKFLOW_YML } from "./pr-visual-recap-workflow.js";
|
|
5
|
+
export { CONNECTION_REFERENCE_MD, RECAP_REFERENCE_FILES, VISUAL_RECAP_SKILL_MD, } from "./skill-content.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC","sourcesContent":["export * from \"./recap.js\";\nexport {\n DEFAULT_PLAN_APP_URL,\n defaultPlanBlocksOut,\n fetchPlanBlockCatalog,\n normalizePlanBlockFormat,\n planActionEndpoint,\n} from \"./plan-blocks.js\";\nexport type {\n FetchPlanBlockCatalogInput,\n FetchPlanBlockCatalogResult,\n PlanBlockFormat,\n} from \"./plan-blocks.js\";\nexport {\n isFirstPartyPlanHost,\n planPublishConfigPath,\n readPlanPublishAuth,\n writePlanPublishAuth,\n} from \"./plan-publish-store.js\";\nexport { PR_VISUAL_RECAP_WORKFLOW_YML } from \"./pr-visual-recap-workflow.js\";\nexport {\n CONNECTION_REFERENCE_MD,\n RECAP_REFERENCE_FILES,\n VISUAL_RECAP_SKILL_MD,\n} from \"./skill-content.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-compatible-endpoint.d.ts","sourceRoot":"","sources":["../src/openai-compatible-endpoint.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAEzD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsB5D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const OPENAI_BASE_URL_ENV_VAR = "OPENAI_BASE_URL";
|
|
2
|
+
export function normalizeOpenAiBaseUrl(value) {
|
|
3
|
+
const trimmed = value.trim();
|
|
4
|
+
if (!trimmed) {
|
|
5
|
+
throw new Error("Endpoint URL is required.");
|
|
6
|
+
}
|
|
7
|
+
let url;
|
|
8
|
+
try {
|
|
9
|
+
url = new URL(trimmed);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error("Endpoint URL must be a valid URL.");
|
|
13
|
+
}
|
|
14
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
15
|
+
throw new Error("Endpoint URL must start with http:// or https://.");
|
|
16
|
+
}
|
|
17
|
+
if (url.username || url.password) {
|
|
18
|
+
throw new Error("Endpoint URL must not include credentials.");
|
|
19
|
+
}
|
|
20
|
+
url.hash = "";
|
|
21
|
+
return url.toString().replace(/\/+$/, "");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=openai-compatible-endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-compatible-endpoint.js","sourceRoot":"","sources":["../src/openai-compatible-endpoint.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAEzD,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["export const OPENAI_BASE_URL_ENV_VAR = \"OPENAI_BASE_URL\";\n\nexport function normalizeOpenAiBaseUrl(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) {\n throw new Error(\"Endpoint URL is required.\");\n }\n\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n throw new Error(\"Endpoint URL must be a valid URL.\");\n }\n\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Error(\"Endpoint URL must start with http:// or https://.\");\n }\n if (url.username || url.password) {\n throw new Error(\"Endpoint URL must not include credentials.\");\n }\n\n url.hash = \"\";\n return url.toString().replace(/\\/+$/, \"\");\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const DEFAULT_PLAN_APP_URL = "https://plan.agent-native.com";
|
|
2
|
+
export type PlanBlockFormat = "reference" | "schema";
|
|
3
|
+
export type FetchPlanBlockCatalogInput = {
|
|
4
|
+
appUrl?: string;
|
|
5
|
+
out?: string;
|
|
6
|
+
format?: PlanBlockFormat;
|
|
7
|
+
fetchFn?: typeof fetch;
|
|
8
|
+
};
|
|
9
|
+
export type FetchPlanBlockCatalogResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
out: string;
|
|
12
|
+
count?: number;
|
|
13
|
+
format: PlanBlockFormat;
|
|
14
|
+
};
|
|
15
|
+
export declare function planActionEndpoint(appUrl: string, action: string): string;
|
|
16
|
+
export declare function normalizePlanBlockFormat(value: string | undefined): PlanBlockFormat;
|
|
17
|
+
export declare function defaultPlanBlocksOut(format: PlanBlockFormat): string;
|
|
18
|
+
export declare function fetchPlanBlockCatalog(input: FetchPlanBlockCatalogInput): Promise<FetchPlanBlockCatalogResult>;
|
|
19
|
+
//# sourceMappingURL=plan-blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-blocks.d.ts","sourceRoot":"","sources":["../src/plan-blocks.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAIpE,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,CAAC;AAErD,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,eAAe,CAIjB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAEpE;AAkDD,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC,CAqDtC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const DEFAULT_PLAN_APP_URL = "https://plan.agent-native.com";
|
|
4
|
+
const PLAN_BLOCKS_HTTP_TIMEOUT_MS = 45_000;
|
|
5
|
+
export function planActionEndpoint(appUrl, action) {
|
|
6
|
+
return `${appUrl.replace(/\/$/, "")}/_agent-native/actions/${action}`;
|
|
7
|
+
}
|
|
8
|
+
export function normalizePlanBlockFormat(value) {
|
|
9
|
+
if (!value || value === "reference")
|
|
10
|
+
return "reference";
|
|
11
|
+
if (value === "schema")
|
|
12
|
+
return "schema";
|
|
13
|
+
throw new Error(`Invalid --format "${value}" (expected reference or schema)`);
|
|
14
|
+
}
|
|
15
|
+
export function defaultPlanBlocksOut(format) {
|
|
16
|
+
return format === "schema" ? "plan-blocks.schema.json" : "plan-blocks.md";
|
|
17
|
+
}
|
|
18
|
+
function sanitizeFetchDetail(value, maxLength) {
|
|
19
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
20
|
+
if (normalized.length <= maxLength)
|
|
21
|
+
return normalized;
|
|
22
|
+
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
23
|
+
}
|
|
24
|
+
function shouldRetryPlanBlocks(status) {
|
|
25
|
+
return (status === 404 ||
|
|
26
|
+
status === 408 ||
|
|
27
|
+
status === 409 ||
|
|
28
|
+
status === 425 ||
|
|
29
|
+
status === 429 ||
|
|
30
|
+
status >= 500);
|
|
31
|
+
}
|
|
32
|
+
function delay(ms) {
|
|
33
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
34
|
+
}
|
|
35
|
+
async function fetchWithTimeout(url, init, fetchFn) {
|
|
36
|
+
return await fetchFn(url, {
|
|
37
|
+
...init,
|
|
38
|
+
signal: init.signal ?? AbortSignal.timeout(PLAN_BLOCKS_HTTP_TIMEOUT_MS),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function renderBlockCatalogOutput(json, format) {
|
|
42
|
+
if (format === "schema") {
|
|
43
|
+
if (!Array.isArray(json.blocks)) {
|
|
44
|
+
throw new Error("get-plan-blocks returned no schema blocks.");
|
|
45
|
+
}
|
|
46
|
+
return `${JSON.stringify({ count: json.count, blocks: json.blocks }, null, 2)}\n`;
|
|
47
|
+
}
|
|
48
|
+
if (typeof json.reference !== "string" || json.reference.length === 0) {
|
|
49
|
+
throw new Error("get-plan-blocks returned no reference text.");
|
|
50
|
+
}
|
|
51
|
+
return json.reference;
|
|
52
|
+
}
|
|
53
|
+
export async function fetchPlanBlockCatalog(input) {
|
|
54
|
+
const fetchFn = input.fetchFn ?? fetch;
|
|
55
|
+
const appUrl = input.appUrl ?? DEFAULT_PLAN_APP_URL;
|
|
56
|
+
const format = input.format ?? "reference";
|
|
57
|
+
const out = input.out ?? defaultPlanBlocksOut(format);
|
|
58
|
+
const endpoint = new URL(planActionEndpoint(appUrl, "get-plan-blocks"));
|
|
59
|
+
endpoint.searchParams.set("format", format);
|
|
60
|
+
let lastError = "";
|
|
61
|
+
for (let attempt = 1; attempt <= 4; attempt += 1) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetchWithTimeout(endpoint.toString(), { method: "GET", headers: { accept: "application/json" } }, fetchFn);
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
const detail = await response.text().catch(() => "");
|
|
66
|
+
lastError = `get-plan-blocks failed ${response.status} ${response.statusText}: ${sanitizeFetchDetail(detail, 500)}`;
|
|
67
|
+
if (attempt < 4 && shouldRetryPlanBlocks(response.status)) {
|
|
68
|
+
await delay(attempt * 1500);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
throw new Error(lastError);
|
|
72
|
+
}
|
|
73
|
+
const json = (await response.json().catch(() => null));
|
|
74
|
+
if (!json)
|
|
75
|
+
throw new Error("get-plan-blocks returned invalid JSON.");
|
|
76
|
+
const text = renderBlockCatalogOutput(json, format);
|
|
77
|
+
fs.writeFileSync(path.resolve(out), text, "utf-8");
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
out,
|
|
81
|
+
count: typeof json.count === "number" ? json.count : undefined,
|
|
82
|
+
format,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
87
|
+
if (attempt < 4) {
|
|
88
|
+
await delay(attempt * 1500);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
throw new Error(lastError);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw new Error(lastError || "get-plan-blocks failed.");
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=plan-blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-blocks.js","sourceRoot":"","sources":["../src/plan-blocks.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport const DEFAULT_PLAN_APP_URL = \"https://plan.agent-native.com\";\n\nconst PLAN_BLOCKS_HTTP_TIMEOUT_MS = 45_000;\n\nexport type PlanBlockFormat = \"reference\" | \"schema\";\n\nexport type FetchPlanBlockCatalogInput = {\n appUrl?: string;\n out?: string;\n format?: PlanBlockFormat;\n fetchFn?: typeof fetch;\n};\n\nexport type FetchPlanBlockCatalogResult = {\n ok: true;\n out: string;\n count?: number;\n format: PlanBlockFormat;\n};\n\nexport function planActionEndpoint(appUrl: string, action: string): string {\n return `${appUrl.replace(/\\/$/, \"\")}/_agent-native/actions/${action}`;\n}\n\nexport function normalizePlanBlockFormat(\n value: string | undefined,\n): PlanBlockFormat {\n if (!value || value === \"reference\") return \"reference\";\n if (value === \"schema\") return \"schema\";\n throw new Error(`Invalid --format \"${value}\" (expected reference or schema)`);\n}\n\nexport function defaultPlanBlocksOut(format: PlanBlockFormat): string {\n return format === \"schema\" ? \"plan-blocks.schema.json\" : \"plan-blocks.md\";\n}\n\nfunction sanitizeFetchDetail(value: string, maxLength: number): string {\n const normalized = value.replace(/\\s+/g, \" \").trim();\n if (normalized.length <= maxLength) return normalized;\n return `${normalized.slice(0, maxLength - 3)}...`;\n}\n\nfunction shouldRetryPlanBlocks(status: number): boolean {\n return (\n status === 404 ||\n status === 408 ||\n status === 409 ||\n status === 425 ||\n status === 429 ||\n status >= 500\n );\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n fetchFn: typeof fetch,\n): Promise<Response> {\n return await fetchFn(url, {\n ...init,\n signal: init.signal ?? AbortSignal.timeout(PLAN_BLOCKS_HTTP_TIMEOUT_MS),\n });\n}\n\nfunction renderBlockCatalogOutput(\n json: { reference?: unknown; blocks?: unknown; count?: unknown },\n format: PlanBlockFormat,\n): string {\n if (format === \"schema\") {\n if (!Array.isArray(json.blocks)) {\n throw new Error(\"get-plan-blocks returned no schema blocks.\");\n }\n return `${JSON.stringify({ count: json.count, blocks: json.blocks }, null, 2)}\\n`;\n }\n if (typeof json.reference !== \"string\" || json.reference.length === 0) {\n throw new Error(\"get-plan-blocks returned no reference text.\");\n }\n return json.reference;\n}\n\nexport async function fetchPlanBlockCatalog(\n input: FetchPlanBlockCatalogInput,\n): Promise<FetchPlanBlockCatalogResult> {\n const fetchFn = input.fetchFn ?? fetch;\n const appUrl = input.appUrl ?? DEFAULT_PLAN_APP_URL;\n const format = input.format ?? \"reference\";\n const out = input.out ?? defaultPlanBlocksOut(format);\n const endpoint = new URL(planActionEndpoint(appUrl, \"get-plan-blocks\"));\n endpoint.searchParams.set(\"format\", format);\n\n let lastError = \"\";\n for (let attempt = 1; attempt <= 4; attempt += 1) {\n try {\n const response = await fetchWithTimeout(\n endpoint.toString(),\n { method: \"GET\", headers: { accept: \"application/json\" } },\n fetchFn,\n );\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n lastError = `get-plan-blocks failed ${response.status} ${\n response.statusText\n }: ${sanitizeFetchDetail(detail, 500)}`;\n if (attempt < 4 && shouldRetryPlanBlocks(response.status)) {\n await delay(attempt * 1500);\n continue;\n }\n throw new Error(lastError);\n }\n\n const json = (await response.json().catch(() => null)) as {\n reference?: unknown;\n blocks?: unknown;\n count?: unknown;\n } | null;\n if (!json) throw new Error(\"get-plan-blocks returned invalid JSON.\");\n const text = renderBlockCatalogOutput(json, format);\n fs.writeFileSync(path.resolve(out), text, \"utf-8\");\n return {\n ok: true,\n out,\n count: typeof json.count === \"number\" ? json.count : undefined,\n format,\n };\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err);\n if (attempt < 4) {\n await delay(attempt * 1500);\n continue;\n }\n throw new Error(lastError);\n }\n }\n\n throw new Error(lastError || \"get-plan-blocks failed.\");\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical publish-token store for the local Plans server.
|
|
3
|
+
*
|
|
4
|
+
* `agent-native connect <hosted-url>` mints a bearer token and writes it into
|
|
5
|
+
* each coding agent's per-client MCP config (`.mcp.json` / Codex `config.toml`
|
|
6
|
+
* via `mcp-config-writers`). Those files are client-specific, so the local Plans
|
|
7
|
+
* server cannot read them for a server-to-server publish.
|
|
8
|
+
*
|
|
9
|
+
* To close that seam, the connect flow ALSO writes a single canonical record to
|
|
10
|
+
* `~/.agent-native/plan-publish.json` whenever it authenticates a first-party
|
|
11
|
+
* Plans app. The local server's `publish-visual-plan` action reads the exact
|
|
12
|
+
* same file (see `templates/plan/server/lib/plan-publish.ts`):
|
|
13
|
+
*
|
|
14
|
+
* { "url": "https://plan.agent-native.com", "token": "<bearer>" }
|
|
15
|
+
*
|
|
16
|
+
* This mirrors the existing device-token precedent in
|
|
17
|
+
* `code-agent-connector.ts` (`~/.agent-native/remote-device.json`): home-dir
|
|
18
|
+
* JSON, env-overridable path, atomic 0600 write. The write is additive — it
|
|
19
|
+
* merges into any existing file rather than clobbering sibling keys — and
|
|
20
|
+
* best-effort, since persisting MCP config is the primary contract and a failed
|
|
21
|
+
* canonical write must never fail the connect.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Absolute path to the canonical publish-token file. Honors
|
|
25
|
+
* `PLAN_PUBLISH_CONFIG_PATH` so connect and the local server agree on the
|
|
26
|
+
* location in tests and custom setups; defaults to
|
|
27
|
+
* `~/.agent-native/plan-publish.json`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function planPublishConfigPath(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Whether `url`'s host is the first-party Agent-Native Plans app whose token
|
|
32
|
+
* we should mirror to the canonical publish file. Only the hosted Plans app
|
|
33
|
+
* (`plan.agent-native.com`) qualifies — mirroring tokens for other
|
|
34
|
+
* agent-native subdomains (assets, mail, …) would silently overwrite the
|
|
35
|
+
* canonical Plans endpoint with the wrong URL+token each time `connect --all`
|
|
36
|
+
* runs last-write-wins. A custom self-hosted origin (ngrok, localhost, a
|
|
37
|
+
* private deployment) is intentionally excluded: the user can still point the
|
|
38
|
+
* server at it via `PLAN_PUBLISH_URL` / `PLAN_PUBLISH_TOKEN` env vars.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isFirstPartyPlanHost(url: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Merge `{ url, token }` into the canonical publish file without clobbering any
|
|
43
|
+
* other keys the file already holds. Best-effort: returns the written path on
|
|
44
|
+
* success, or `null` if the write failed or the inputs were unusable.
|
|
45
|
+
*
|
|
46
|
+
* `filePath` is injectable for tests; production callers omit it and get the
|
|
47
|
+
* env-overridable home-dir path.
|
|
48
|
+
*/
|
|
49
|
+
export declare function writePlanPublishAuth(params: {
|
|
50
|
+
url: string;
|
|
51
|
+
token: string;
|
|
52
|
+
}, filePath?: string): string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Read the canonical Plans publish auth written by `agent-native connect`.
|
|
55
|
+
* Returns `null` for missing/corrupt/incomplete files so callers can treat the
|
|
56
|
+
* publish token as optional and guide the user to reconnect.
|
|
57
|
+
*/
|
|
58
|
+
export declare function readPlanPublishAuth(filePath?: string): {
|
|
59
|
+
url: string;
|
|
60
|
+
token: string;
|
|
61
|
+
} | null;
|
|
62
|
+
//# sourceMappingURL=plan-publish-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-publish-store.d.ts","sourceRoot":"","sources":["../src/plan-publish-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOzD;AAMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACtC,QAAQ,GAAE,MAAgC,GACzC,MAAM,GAAG,IAAI,CAmCf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,MAAgC,GACzC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBvC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical publish-token store for the local Plans server.
|
|
3
|
+
*
|
|
4
|
+
* `agent-native connect <hosted-url>` mints a bearer token and writes it into
|
|
5
|
+
* each coding agent's per-client MCP config (`.mcp.json` / Codex `config.toml`
|
|
6
|
+
* via `mcp-config-writers`). Those files are client-specific, so the local Plans
|
|
7
|
+
* server cannot read them for a server-to-server publish.
|
|
8
|
+
*
|
|
9
|
+
* To close that seam, the connect flow ALSO writes a single canonical record to
|
|
10
|
+
* `~/.agent-native/plan-publish.json` whenever it authenticates a first-party
|
|
11
|
+
* Plans app. The local server's `publish-visual-plan` action reads the exact
|
|
12
|
+
* same file (see `templates/plan/server/lib/plan-publish.ts`):
|
|
13
|
+
*
|
|
14
|
+
* { "url": "https://plan.agent-native.com", "token": "<bearer>" }
|
|
15
|
+
*
|
|
16
|
+
* This mirrors the existing device-token precedent in
|
|
17
|
+
* `code-agent-connector.ts` (`~/.agent-native/remote-device.json`): home-dir
|
|
18
|
+
* JSON, env-overridable path, atomic 0600 write. The write is additive — it
|
|
19
|
+
* merges into any existing file rather than clobbering sibling keys — and
|
|
20
|
+
* best-effort, since persisting MCP config is the primary contract and a failed
|
|
21
|
+
* canonical write must never fail the connect.
|
|
22
|
+
*/
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import os from "node:os";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
/** Matches the env override read by `templates/plan/server/lib/plan-publish.ts`. */
|
|
27
|
+
const CONFIG_PATH_ENV = "PLAN_PUBLISH_CONFIG_PATH";
|
|
28
|
+
/**
|
|
29
|
+
* Absolute path to the canonical publish-token file. Honors
|
|
30
|
+
* `PLAN_PUBLISH_CONFIG_PATH` so connect and the local server agree on the
|
|
31
|
+
* location in tests and custom setups; defaults to
|
|
32
|
+
* `~/.agent-native/plan-publish.json`.
|
|
33
|
+
*/
|
|
34
|
+
export function planPublishConfigPath() {
|
|
35
|
+
return path.resolve(process.env[CONFIG_PATH_ENV] ??
|
|
36
|
+
path.join(os.homedir(), ".agent-native", "plan-publish.json"));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether `url`'s host is the first-party Agent-Native Plans app whose token
|
|
40
|
+
* we should mirror to the canonical publish file. Only the hosted Plans app
|
|
41
|
+
* (`plan.agent-native.com`) qualifies — mirroring tokens for other
|
|
42
|
+
* agent-native subdomains (assets, mail, …) would silently overwrite the
|
|
43
|
+
* canonical Plans endpoint with the wrong URL+token each time `connect --all`
|
|
44
|
+
* runs last-write-wins. A custom self-hosted origin (ngrok, localhost, a
|
|
45
|
+
* private deployment) is intentionally excluded: the user can still point the
|
|
46
|
+
* server at it via `PLAN_PUBLISH_URL` / `PLAN_PUBLISH_TOKEN` env vars.
|
|
47
|
+
*/
|
|
48
|
+
export function isFirstPartyPlanHost(url) {
|
|
49
|
+
try {
|
|
50
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
51
|
+
return host === "plan.agent-native.com";
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function stripTrailingSlash(url) {
|
|
58
|
+
return url.replace(/\/+$/, "");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge `{ url, token }` into the canonical publish file without clobbering any
|
|
62
|
+
* other keys the file already holds. Best-effort: returns the written path on
|
|
63
|
+
* success, or `null` if the write failed or the inputs were unusable.
|
|
64
|
+
*
|
|
65
|
+
* `filePath` is injectable for tests; production callers omit it and get the
|
|
66
|
+
* env-overridable home-dir path.
|
|
67
|
+
*/
|
|
68
|
+
export function writePlanPublishAuth(params, filePath = planPublishConfigPath()) {
|
|
69
|
+
const url = stripTrailingSlash((params.url ?? "").trim());
|
|
70
|
+
const token = (params.token ?? "").trim();
|
|
71
|
+
if (!url || !token)
|
|
72
|
+
return null;
|
|
73
|
+
try {
|
|
74
|
+
let existing = {};
|
|
75
|
+
try {
|
|
76
|
+
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
77
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
78
|
+
existing = raw;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// No existing file (or unreadable/corrupt) — start fresh.
|
|
83
|
+
}
|
|
84
|
+
const merged = {
|
|
85
|
+
...existing,
|
|
86
|
+
url,
|
|
87
|
+
token,
|
|
88
|
+
updatedAt: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
91
|
+
const tmp = `${filePath}.tmp`;
|
|
92
|
+
fs.writeFileSync(tmp, JSON.stringify(merged, null, 2) + "\n", {
|
|
93
|
+
mode: 0o600,
|
|
94
|
+
});
|
|
95
|
+
fs.renameSync(tmp, filePath);
|
|
96
|
+
return filePath;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Best-effort: the per-client MCP config is the primary write. A failed
|
|
100
|
+
// canonical write must never fail the connect flow.
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Read the canonical Plans publish auth written by `agent-native connect`.
|
|
106
|
+
* Returns `null` for missing/corrupt/incomplete files so callers can treat the
|
|
107
|
+
* publish token as optional and guide the user to reconnect.
|
|
108
|
+
*/
|
|
109
|
+
export function readPlanPublishAuth(filePath = planPublishConfigPath()) {
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
112
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const record = parsed;
|
|
116
|
+
const urlValue = record.url ?? record.baseUrl ?? record.hostedUrl;
|
|
117
|
+
const tokenValue = record.token ?? record.accessToken ?? record.bearerToken;
|
|
118
|
+
const url = typeof urlValue === "string" ? urlValue.trim() : "";
|
|
119
|
+
const token = typeof tokenValue === "string" ? tokenValue.trim() : "";
|
|
120
|
+
if (!url || !token)
|
|
121
|
+
return null;
|
|
122
|
+
return { url: stripTrailingSlash(url), token };
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=plan-publish-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-publish-store.js","sourceRoot":"","sources":["../src/plan-publish-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Canonical publish-token store for the local Plans server.\n *\n * `agent-native connect <hosted-url>` mints a bearer token and writes it into\n * each coding agent's per-client MCP config (`.mcp.json` / Codex `config.toml`\n * via `mcp-config-writers`). Those files are client-specific, so the local Plans\n * server cannot read them for a server-to-server publish.\n *\n * To close that seam, the connect flow ALSO writes a single canonical record to\n * `~/.agent-native/plan-publish.json` whenever it authenticates a first-party\n * Plans app. The local server's `publish-visual-plan` action reads the exact\n * same file (see `templates/plan/server/lib/plan-publish.ts`):\n *\n * { \"url\": \"https://plan.agent-native.com\", \"token\": \"<bearer>\" }\n *\n * This mirrors the existing device-token precedent in\n * `code-agent-connector.ts` (`~/.agent-native/remote-device.json`): home-dir\n * JSON, env-overridable path, atomic 0600 write. The write is additive — it\n * merges into any existing file rather than clobbering sibling keys — and\n * best-effort, since persisting MCP config is the primary contract and a failed\n * canonical write must never fail the connect.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** Matches the env override read by `templates/plan/server/lib/plan-publish.ts`. */\nconst CONFIG_PATH_ENV = \"PLAN_PUBLISH_CONFIG_PATH\";\n\n/**\n * Absolute path to the canonical publish-token file. Honors\n * `PLAN_PUBLISH_CONFIG_PATH` so connect and the local server agree on the\n * location in tests and custom setups; defaults to\n * `~/.agent-native/plan-publish.json`.\n */\nexport function planPublishConfigPath(): string {\n return path.resolve(\n process.env[CONFIG_PATH_ENV] ??\n path.join(os.homedir(), \".agent-native\", \"plan-publish.json\"),\n );\n}\n\n/**\n * Whether `url`'s host is the first-party Agent-Native Plans app whose token\n * we should mirror to the canonical publish file. Only the hosted Plans app\n * (`plan.agent-native.com`) qualifies — mirroring tokens for other\n * agent-native subdomains (assets, mail, …) would silently overwrite the\n * canonical Plans endpoint with the wrong URL+token each time `connect --all`\n * runs last-write-wins. A custom self-hosted origin (ngrok, localhost, a\n * private deployment) is intentionally excluded: the user can still point the\n * server at it via `PLAN_PUBLISH_URL` / `PLAN_PUBLISH_TOKEN` env vars.\n */\nexport function isFirstPartyPlanHost(url: string): boolean {\n try {\n const host = new URL(url).hostname.toLowerCase();\n return host === \"plan.agent-native.com\";\n } catch {\n return false;\n }\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Merge `{ url, token }` into the canonical publish file without clobbering any\n * other keys the file already holds. Best-effort: returns the written path on\n * success, or `null` if the write failed or the inputs were unusable.\n *\n * `filePath` is injectable for tests; production callers omit it and get the\n * env-overridable home-dir path.\n */\nexport function writePlanPublishAuth(\n params: { url: string; token: string },\n filePath: string = planPublishConfigPath(),\n): string | null {\n const url = stripTrailingSlash((params.url ?? \"\").trim());\n const token = (params.token ?? \"\").trim();\n if (!url || !token) return null;\n\n try {\n let existing: Record<string, unknown> = {};\n try {\n const raw = JSON.parse(fs.readFileSync(filePath, \"utf-8\")) as unknown;\n if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n existing = raw as Record<string, unknown>;\n }\n } catch {\n // No existing file (or unreadable/corrupt) — start fresh.\n }\n\n const merged = {\n ...existing,\n url,\n token,\n updatedAt: new Date().toISOString(),\n };\n\n fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });\n const tmp = `${filePath}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(merged, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n fs.renameSync(tmp, filePath);\n return filePath;\n } catch {\n // Best-effort: the per-client MCP config is the primary write. A failed\n // canonical write must never fail the connect flow.\n return null;\n }\n}\n\n/**\n * Read the canonical Plans publish auth written by `agent-native connect`.\n * Returns `null` for missing/corrupt/incomplete files so callers can treat the\n * publish token as optional and guide the user to reconnect.\n */\nexport function readPlanPublishAuth(\n filePath: string = planPublishConfigPath(),\n): { url: string; token: string } | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(filePath, \"utf-8\")) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n const record = parsed as Record<string, unknown>;\n const urlValue = record.url ?? record.baseUrl ?? record.hostedUrl;\n const tokenValue = record.token ?? record.accessToken ?? record.bearerToken;\n const url = typeof urlValue === \"string\" ? urlValue.trim() : \"\";\n const token = typeof tokenValue === \"string\" ? tokenValue.trim() : \"\";\n if (!url || !token) return null;\n return { url: stripTrailingSlash(url), token };\n } catch {\n return null;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-visual-recap-workflow.d.ts","sourceRoot":"","sources":["../src/pr-visual-recap-workflow.ts"],"names":[],"mappings":"AAUA,uEAAuE;AACvE,eAAO,MAAM,4BAA4B,QAAqC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const workflowPath = path.basename(moduleDir) === "dist"
|
|
6
|
+
? path.join(moduleDir, "workflows", "pr-visual-recap.yml")
|
|
7
|
+
: path.resolve(moduleDir, "../../../.github/workflows/pr-visual-recap.yml");
|
|
8
|
+
/** Canonical PR Visual Recap workflow bundled by the CLI installer. */
|
|
9
|
+
export const PR_VISUAL_RECAP_WORKFLOW_YML = readFileSync(workflowPath, "utf8");
|
|
10
|
+
//# sourceMappingURL=pr-visual-recap-workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-visual-recap-workflow.js","sourceRoot":"","sources":["../src/pr-visual-recap-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,YAAY,GAChB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,MAAM;IACjC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,qBAAqB,CAAC;IAC1D,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,gDAAgD,CAAC,CAAC;AAEhF,uEAAuE;AACvE,MAAM,CAAC,MAAM,4BAA4B,GAAG,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\nconst workflowPath =\n path.basename(moduleDir) === \"dist\"\n ? path.join(moduleDir, \"workflows\", \"pr-visual-recap.yml\")\n : path.resolve(moduleDir, \"../../../.github/workflows/pr-visual-recap.yml\");\n\n/** Canonical PR Visual Recap workflow bundled by the CLI installer. */\nexport const PR_VISUAL_RECAP_WORKFLOW_YML = readFileSync(workflowPath, \"utf8\");\n"]}
|