@shipeasy/openapi 1.6.0 → 2.1.0

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 (151) hide show
  1. package/README.md +37 -0
  2. package/dist/chunk-EPLLHLZN.js +1616 -0
  3. package/dist/chunk-EPLLHLZN.js.map +1 -0
  4. package/dist/chunk-SPH5HWCD.js +3326 -0
  5. package/dist/chunk-SPH5HWCD.js.map +1 -0
  6. package/dist/client-pVkoo3Bf.d.ts +11186 -0
  7. package/dist/client.d.ts +1 -0
  8. package/dist/client.js +215 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/custom.d.ts +64 -0
  11. package/dist/custom.js +292 -0
  12. package/dist/custom.js.map +1 -0
  13. package/dist/index.d.ts +29 -41
  14. package/dist/index.js +258 -4375
  15. package/dist/index.js.map +1 -0
  16. package/dist/node-context.d.ts +81 -33
  17. package/dist/node-context.js +98 -44
  18. package/dist/node-context.js.map +1 -0
  19. package/dist/schemas.d.ts +7920 -0
  20. package/dist/schemas.js +1083 -0
  21. package/dist/schemas.js.map +1 -0
  22. package/openapi.json +15297 -3044
  23. package/openapi.yaml +13979 -0
  24. package/package.json +33 -26
  25. package/spec/components/errors.yaml +53 -0
  26. package/spec/components/parameters.yaml +24 -0
  27. package/spec/components/responses.yaml +107 -0
  28. package/spec/components/schemas/alert-rules.yaml +175 -0
  29. package/spec/components/schemas/attributes.yaml +157 -0
  30. package/spec/components/schemas/common.yaml +87 -0
  31. package/spec/components/schemas/configs.yaml +371 -0
  32. package/spec/components/schemas/connectors.yaml +593 -0
  33. package/spec/components/schemas/errors-api.yaml +209 -0
  34. package/spec/components/schemas/events.yaml +321 -0
  35. package/spec/components/schemas/experiments.yaml +865 -0
  36. package/spec/components/schemas/gate-templates.yaml +198 -0
  37. package/spec/components/schemas/gates.yaml +890 -0
  38. package/spec/components/schemas/i18n.yaml +478 -0
  39. package/spec/components/schemas/keys.yaml +186 -0
  40. package/spec/components/schemas/killswitches.yaml +309 -0
  41. package/spec/components/schemas/metrics.yaml +547 -0
  42. package/spec/components/schemas/ops.yaml +1011 -0
  43. package/spec/components/schemas/projects.yaml +222 -0
  44. package/spec/components/schemas/search.yaml +46 -0
  45. package/spec/components/schemas/universes.yaml +127 -0
  46. package/spec/openapi.yaml +348 -0
  47. package/spec/paths/alert-rules.yaml +250 -0
  48. package/spec/paths/attributes.yaml +261 -0
  49. package/spec/paths/configs.yaml +744 -0
  50. package/spec/paths/connectors.yaml +748 -0
  51. package/spec/paths/errors.yaml +382 -0
  52. package/spec/paths/events.yaml +366 -0
  53. package/spec/paths/experiments.yaml +777 -0
  54. package/spec/paths/gate-templates.yaml +263 -0
  55. package/spec/paths/gates.yaml +451 -0
  56. package/spec/paths/i18n.yaml +544 -0
  57. package/spec/paths/keys.yaml +181 -0
  58. package/spec/paths/killswitches.yaml +557 -0
  59. package/spec/paths/metrics.yaml +302 -0
  60. package/spec/paths/ops.yaml +446 -0
  61. package/spec/paths/projects.yaml +210 -0
  62. package/spec/paths/search.yaml +62 -0
  63. package/spec/paths/universes.yaml +247 -0
  64. package/dist/operations/attributes.d.ts +0 -33
  65. package/dist/operations/attributes.js +0 -16
  66. package/dist/operations/cli-adapter.d.ts +0 -35
  67. package/dist/operations/cli-adapter.js +0 -91
  68. package/dist/operations/coerce.d.ts +0 -32
  69. package/dist/operations/coerce.js +0 -53
  70. package/dist/operations/configs.d.ts +0 -28
  71. package/dist/operations/configs.js +0 -119
  72. package/dist/operations/docs.d.ts +0 -37
  73. package/dist/operations/docs.js +0 -39
  74. package/dist/operations/events.d.ts +0 -28
  75. package/dist/operations/events.js +0 -121
  76. package/dist/operations/experiments.d.ts +0 -28
  77. package/dist/operations/experiments.js +0 -280
  78. package/dist/operations/gates.d.ts +0 -28
  79. package/dist/operations/gates.js +0 -132
  80. package/dist/operations/i18n.d.ts +0 -28
  81. package/dist/operations/i18n.js +0 -121
  82. package/dist/operations/index.d.ts +0 -63
  83. package/dist/operations/index.js +0 -1751
  84. package/dist/operations/killswitches.d.ts +0 -28
  85. package/dist/operations/killswitches.js +0 -126
  86. package/dist/operations/mcp-adapter.d.ts +0 -35
  87. package/dist/operations/mcp-adapter.js +0 -94
  88. package/dist/operations/metrics.d.ts +0 -30
  89. package/dist/operations/metrics.js +0 -114
  90. package/dist/operations/ops.d.ts +0 -28
  91. package/dist/operations/ops.js +0 -313
  92. package/dist/operations/projects.d.ts +0 -28
  93. package/dist/operations/projects.js +0 -36
  94. package/dist/operations/sdk-docs.d.ts +0 -28
  95. package/dist/operations/sdk-docs.js +0 -160
  96. package/dist/operations/types.d.ts +0 -138
  97. package/dist/operations/types.js +0 -9
  98. package/dist/operations/universes.d.ts +0 -28
  99. package/dist/operations/universes.js +0 -93
  100. package/dist/operations/util.d.ts +0 -33
  101. package/dist/operations/util.js +0 -9
  102. package/dist/resources/alert-rules.d.ts +0 -50
  103. package/dist/resources/alert-rules.js +0 -75
  104. package/dist/resources/attributes.d.ts +0 -19
  105. package/dist/resources/attributes.js +0 -10
  106. package/dist/resources/configs.d.ts +0 -681
  107. package/dist/resources/configs.js +0 -509
  108. package/dist/resources/events.d.ts +0 -61
  109. package/dist/resources/events.js +0 -42
  110. package/dist/resources/experiments.d.ts +0 -849
  111. package/dist/resources/experiments.js +0 -707
  112. package/dist/resources/gates.d.ts +0 -808
  113. package/dist/resources/gates.js +0 -522
  114. package/dist/resources/i18n.d.ts +0 -84
  115. package/dist/resources/i18n.js +0 -60
  116. package/dist/resources/index.d.ts +0 -2767
  117. package/dist/resources/index.js +0 -2563
  118. package/dist/resources/killswitches.d.ts +0 -436
  119. package/dist/resources/killswitches.js +0 -514
  120. package/dist/resources/metrics.d.ts +0 -54
  121. package/dist/resources/metrics.js +0 -40
  122. package/dist/resources/ops.d.ts +0 -88
  123. package/dist/resources/ops.js +0 -36
  124. package/dist/resources/projects.d.ts +0 -54
  125. package/dist/resources/projects.js +0 -14
  126. package/dist/resources/universes.d.ts +0 -185
  127. package/dist/resources/universes.js +0 -234
  128. package/dist/schemas/alert-rules.d.ts +0 -65
  129. package/dist/schemas/alert-rules.js +0 -37
  130. package/dist/schemas/configs.d.ts +0 -154
  131. package/dist/schemas/configs.js +0 -134
  132. package/dist/schemas/constants.d.ts +0 -6
  133. package/dist/schemas/constants.js +0 -7
  134. package/dist/schemas/experiments.d.ts +0 -229
  135. package/dist/schemas/experiments.js +0 -221
  136. package/dist/schemas/folder.d.ts +0 -10
  137. package/dist/schemas/folder.js +0 -8
  138. package/dist/schemas/gates.d.ts +0 -386
  139. package/dist/schemas/gates.js +0 -190
  140. package/dist/schemas/index.d.ts +0 -11
  141. package/dist/schemas/index.js +0 -762
  142. package/dist/schemas/killswitches.d.ts +0 -110
  143. package/dist/schemas/killswitches.js +0 -226
  144. package/dist/schemas/metric-name.d.ts +0 -5
  145. package/dist/schemas/metric-name.js +0 -6
  146. package/dist/schemas/pagination.d.ts +0 -37
  147. package/dist/schemas/pagination.js +0 -44
  148. package/dist/schemas/universes.d.ts +0 -35
  149. package/dist/schemas/universes.js +0 -55
  150. package/dist/transport.d.ts +0 -30
  151. package/dist/transport.js +0 -44
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import { zErrorCode } from \"./generated/zod.gen.js\";\nimport type { Error as ApiErrorBody, ErrorCode } from \"./generated/types.gen.js\";\n\nexport type { ErrorCode };\nexport type { ApiErrorBody };\n\n/**\n * The canonical `ErrorCode` catalogue as a runtime array — derived from the\n * generated Zod enum so it can never drift from the spec. Mirrors\n * `@shipeasy/core`'s `ErrorCode`; the parity test (`scripts/check-drift.mjs`)\n * asserts every `x-error-codes` entry in the spec is one of these.\n */\nexport const ERROR_CODES = zErrorCode.options as readonly ErrorCode[];\n\n/** Type guard for the uniform `{ error, code?, detail? }` error envelope. */\nexport function isApiErrorBody(value: unknown): value is ApiErrorBody {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"error\" in value &&\n typeof (value as { error: unknown }).error === \"string\"\n );\n}\n\n/** Narrow an error body to a known `ErrorCode`, or `undefined` if uncatalogued. */\nexport function errorCodeOf(value: unknown): ErrorCode | undefined {\n if (!isApiErrorBody(value)) return undefined;\n const code = value.code;\n return code && (ERROR_CODES as readonly string[]).includes(code) ? code : undefined;\n}\n\n/** Which binary is reporting the failure — determines which command to tell the caller to run. */\nexport type AuthSurface = \"cli\" | \"mcp\";\n\nconst REAUTH_COMMAND: Record<AuthSurface, string> = {\n cli: \"shipeasy login --force\",\n mcp: \"shipeasy-mcp install --force\",\n};\n\n/**\n * Structured, agent-parseable remediation text for an admin-API auth failure\n * (401/403) — shared by the CLI's and MCP's error printers so every command\n * and every tool call reports auth failures the same way. The leading\n * `AUTH_REQUIRED:` / `AUTH_FORBIDDEN:` token is a stable marker a calling\n * agent can grep for; everything after it is human-readable remediation.\n *\n * Returns `undefined` for non-auth statuses so callers fall back to their\n * normal error formatting.\n */\nexport function formatAuthFailure(\n surface: AuthSurface,\n status: number,\n message: string,\n): string | undefined {\n const reauth = REAUTH_COMMAND[surface];\n if (status === 401) {\n return [\n `AUTH_REQUIRED: ${message} (401)`,\n ``,\n `Cause: the request's Shipeasy credentials are missing, invalid, or expired.`,\n `Fix:`,\n ` 1. Run: ${reauth}`,\n ` 2. Retry the command that failed.`,\n ].join(\"\\n\");\n }\n if (status === 403) {\n return [\n `AUTH_FORBIDDEN: ${message} (403)`,\n ``,\n `Cause: the credentials are valid but don't have access to this project or resource.`,\n `Fix:`,\n ` 1. Confirm the bound project is correct (check the .shipeasy file, or run: shipeasy root --json).`,\n ` 2. If it's the wrong project, run: shipeasy bind <project_id>.`,\n ` 3. If it's the right project, ask a workspace admin to grant access — retrying will not help.`,\n ].join(\"\\n\");\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYO,IAAM,cAAc,WAAW;AAG/B,SAAS,eAAe,OAAuC;AACpE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAA6B,UAAU;AAEnD;AAGO,SAAS,YAAY,OAAuC;AACjE,MAAI,CAAC,eAAe,KAAK,EAAG,QAAO;AACnC,QAAM,OAAO,MAAM;AACnB,SAAO,QAAS,YAAkC,SAAS,IAAI,IAAI,OAAO;AAC5E;AAKA,IAAM,iBAA8C;AAAA,EAClD,KAAK;AAAA,EACL,KAAK;AACP;AAYO,SAAS,kBACd,SACA,QACA,SACoB;AACpB,QAAM,SAAS,eAAe,OAAO;AACrC,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,MACL,kBAAkB,OAAO;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,MAAM;AAAA,MACnB;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,MACL,mBAAmB,OAAO;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACA,SAAO;AACT;","names":[]}
@@ -1,37 +1,85 @@
1
- interface ConsumerContext {
2
- /** SDK token used as `X-SDK-Key`. */
3
- token: string;
4
- /** Resolved project id used as `X-Project-Id`. */
5
- projectId: string;
6
- /** Directory that held the `.shipeasy` binding, if any. */
7
- boundDir: string | null;
8
- /** Path to the XDG config file that supplied the session, if read. */
9
- configPath: string;
10
- /** Owner email from the session config, if present. */
11
- userEmail: string | null;
12
- /** True when a `.shipeasy` binding (or `--project`/env override) supplied the project id. */
13
- bound: boolean;
14
- }
15
- /** XDG config path for the CLI/MCP session (`~/.config/shipeasy/config.json`). */
16
- declare function configPath(): string;
17
- /** Walk up from `dir` (like `.git`) to find the nearest `.shipeasy` file. */
18
- declare function findBoundDir(dir: string): string | null;
19
- /** The bound project id from the nearest `.shipeasy`, or undefined. */
20
- declare function getBoundProjectId(dir?: string): string | undefined;
21
- interface ResolveOptions {
22
- /** Explicit `--project` override (highest precedence). */
23
- projectOverride?: string;
24
- /** Directory to resolve the binding from (defaults to cwd). */
25
- cwd?: string;
26
- }
27
1
  /**
28
- * Resolve the consumer context once: env overrides → `.shipeasy` binding →
29
- * session config. Returns null if there is no usable session token.
2
+ * The shared `.shipeasy` project-binding file — the single source of truth for
3
+ * both `@shipeasy/cli` and `@shipeasy/mcp`. Lives under `./node-context` because
4
+ * it touches `node:fs`: **never import it from the worker-bundled graph** (the
5
+ * openapi root/`./custom` entries stay fs-free and worker-safe).
30
6
  *
31
- * Precedence for the project id: `projectOverride` → `SHIPEASY_PROJECT_ID` →
32
- * `.shipeasy` → session default. The token comes from `SHIPEASY_CLI_TOKEN` or
33
- * the session config.
7
+ * Model: **one `.shipeasy` per project**. The folder that holds the file IS the
8
+ * project root. Reads walk up from the cwd to the *nearest* file (like git and
9
+ * `.git`), so a subproject's own `.shipeasy` shadows any ancestor and the walk
10
+ * stops there. Writes (`bindProject`, `mergeDetected`) target the LOCAL dir they
11
+ * are given — they never reach up to reuse an ancestor — so each onboarded
12
+ * subproject gets its own file.
13
+ */
14
+ declare const FILENAME = ".shipeasy";
15
+ /** Current `.shipeasy` schema version. Absent in a file → treat as legacy (1). */
16
+ declare const SCHEMA_VERSION = 1;
17
+ interface ProjectConfig {
18
+ /** Shape version; lets future changes stay backward-compatible. */
19
+ schema_version?: number;
20
+ /**
21
+ * The Shipeasy project this directory pushes to. Required for any mutating
22
+ * CLI/MCP operation. Stops a session logged into project A from silently
23
+ * writing to project B because someone's cwd is wrong.
24
+ */
25
+ project_id?: string;
26
+ /** Human-readable project name — display only, not authoritative. */
27
+ project_name?: string;
28
+ /** Detected language (e.g. `typescript`, `python`) — written by `detect`. */
29
+ language?: string;
30
+ /** Docs SDK handle — the value `shipeasy docs` defaults to when `--sdk` is omitted. */
31
+ sdk?: string;
32
+ /** Detected frameworks (e.g. `["nextjs", "react"]`) — written by `detect`. */
33
+ frameworks?: string[];
34
+ i18n?: {
35
+ client_key?: string;
36
+ };
37
+ }
38
+ /**
39
+ * Walk up from `dir` until a `.shipeasy` file is found, like git does for
40
+ * `.git`. Returns the directory containing the file, or `null` if the search
41
+ * hits the filesystem root without finding one.
42
+ */
43
+ declare function findProjectConfigDir(dir: string): string | null;
44
+ /** Sync read of the nearest `.shipeasy` (walking up). Empty object if none. */
45
+ declare function readProjectConfigSync(dir: string): ProjectConfig;
46
+ /** Async read of the nearest `.shipeasy` (walking up). Empty object if none. */
47
+ declare function readProjectConfig(dir: string): Promise<ProjectConfig>;
48
+ /** Write `cfg` to `dir/.shipeasy` verbatim (stamping schema_version if absent). */
49
+ declare function writeProjectConfig(dir: string, cfg: ProjectConfig): void;
50
+ /** Project id from the nearest `.shipeasy` (sync). */
51
+ declare function getBoundProjectId(dir: string): string | undefined;
52
+ /** Alias kept for the MCP call sites. */
53
+ declare const getBoundProjectIdSync: typeof getBoundProjectId;
54
+ /** Docs SDK handle from the nearest `.shipeasy` (sync) — the `docs` default. */
55
+ declare function getBoundSdk(dir: string): string | undefined;
56
+ declare function getI18nClientKey(dir: string): string | undefined;
57
+ declare function saveI18nClientKey(dir: string, key: string): void;
58
+ /**
59
+ * Bind the LOCAL directory `dir` to `projectId` — writes `dir/.shipeasy`
60
+ * directly (create, or merge over an existing LOCAL file to preserve `sdk` /
61
+ * `i18n` / detection fields). It does NOT walk up to reuse an ancestor file:
62
+ * one project per folder, so binding a subproject never mutates the repo root.
63
+ */
64
+ declare function bindProject(dir: string, projectId: string, projectName?: string): {
65
+ path: string;
66
+ created: boolean;
67
+ };
68
+ /** Alias kept for the MCP call sites. */
69
+ declare const bindProjectSync: typeof bindProject;
70
+ /**
71
+ * Non-destructively merge detected language/sdk/frameworks into the LOCAL
72
+ * `dir/.shipeasy` (create it if absent). Never touches `project_id` or any
73
+ * other field — this is what `shipeasy detect` calls per install target so the
74
+ * `docs` default-sdk works even before the project is bound.
34
75
  */
35
- declare function resolveConsumerContext(opts?: ResolveOptions): ConsumerContext | null;
76
+ declare function mergeDetected(dir: string, detected: {
77
+ language?: string;
78
+ sdk?: string;
79
+ frameworks?: string[];
80
+ }): {
81
+ path: string;
82
+ created: boolean;
83
+ };
36
84
 
37
- export { type ConsumerContext, type ResolveOptions, configPath, findBoundDir, getBoundProjectId, resolveConsumerContext };
85
+ export { FILENAME, type ProjectConfig, SCHEMA_VERSION, bindProject, bindProjectSync, findProjectConfigDir, getBoundProjectId, getBoundProjectIdSync, getBoundSdk, getI18nClientKey, mergeDetected, readProjectConfig, readProjectConfigSync, saveI18nClientKey, writeProjectConfig };
@@ -1,56 +1,110 @@
1
- // src/node-context.ts
2
- import { existsSync, readFileSync } from "fs";
3
- import { homedir } from "os";
1
+ // src/node-context/project-config.ts
2
+ import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { readFile } from "fs/promises";
4
4
  import { dirname, join, resolve } from "path";
5
- function configPath() {
6
- const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
7
- return join(base, "shipeasy", "config.json");
5
+ var FILENAME = ".shipeasy";
6
+ var SCHEMA_VERSION = 1;
7
+ function configPath(dir) {
8
+ return join(dir, FILENAME);
8
9
  }
9
- function readJson(path) {
10
- if (!existsSync(path)) return null;
10
+ function findProjectConfigDir(dir) {
11
+ let current = resolve(dir);
12
+ while (true) {
13
+ if (existsSync(join(current, FILENAME))) return current;
14
+ const parent = dirname(current);
15
+ if (parent === current) return null;
16
+ current = parent;
17
+ }
18
+ }
19
+ function parse(raw) {
20
+ try {
21
+ return JSON.parse(raw);
22
+ } catch {
23
+ return {};
24
+ }
25
+ }
26
+ function readProjectConfigSync(dir) {
27
+ const found = findProjectConfigDir(dir);
28
+ if (!found) return {};
29
+ try {
30
+ return parse(readFileSync(join(found, FILENAME), "utf8"));
31
+ } catch {
32
+ return {};
33
+ }
34
+ }
35
+ async function readProjectConfig(dir) {
36
+ const found = findProjectConfigDir(dir);
37
+ if (!found) return {};
11
38
  try {
12
- return JSON.parse(readFileSync(path, "utf8"));
39
+ return parse(await readFile(join(found, FILENAME), "utf8"));
13
40
  } catch {
14
- return null;
41
+ return {};
15
42
  }
16
43
  }
17
- function findBoundDir(dir) {
18
- let cur = resolve(dir);
19
- for (; ; ) {
20
- if (existsSync(join(cur, ".shipeasy"))) return cur;
21
- const parent = dirname(cur);
22
- if (parent === cur) return null;
23
- cur = parent;
44
+ function writeProjectConfig(dir, cfg) {
45
+ const withVersion = { schema_version: SCHEMA_VERSION, ...cfg };
46
+ writeFileSync(configPath(dir), JSON.stringify(withVersion, null, 2) + "\n", "utf8");
47
+ }
48
+ function readLocalConfig(dir) {
49
+ const p = configPath(dir);
50
+ if (!existsSync(p)) return {};
51
+ try {
52
+ return parse(readFileSync(p, "utf8"));
53
+ } catch {
54
+ return {};
24
55
  }
25
56
  }
26
- function getBoundProjectId(dir = process.cwd()) {
27
- const boundDir = findBoundDir(dir);
28
- if (!boundDir) return void 0;
29
- return readJson(join(boundDir, ".shipeasy"))?.project_id;
30
- }
31
- function resolveConsumerContext(opts = {}) {
32
- const cwd = opts.cwd ?? process.cwd();
33
- const cfgPath = configPath();
34
- const cfg = readJson(cfgPath) ?? {};
35
- const token = process.env.SHIPEASY_CLI_TOKEN || cfg.cli_token;
36
- if (!token) return null;
37
- const boundDir = findBoundDir(cwd);
38
- const boundId = boundDir ? readJson(join(boundDir, ".shipeasy"))?.project_id : void 0;
39
- const envProject = process.env.SHIPEASY_PROJECT_ID;
40
- const projectId = opts.projectOverride || envProject || boundId || cfg.project_id;
41
- if (!projectId) return null;
42
- return {
43
- token,
44
- projectId,
45
- boundDir: boundId ? boundDir : null,
46
- configPath: cfgPath,
47
- userEmail: cfg.user_email ?? null,
48
- bound: Boolean(opts.projectOverride || envProject || boundId)
49
- };
57
+ function getBoundProjectId(dir) {
58
+ return readProjectConfigSync(dir).project_id;
59
+ }
60
+ var getBoundProjectIdSync = getBoundProjectId;
61
+ function getBoundSdk(dir) {
62
+ return readProjectConfigSync(dir).sdk;
63
+ }
64
+ function getI18nClientKey(dir) {
65
+ return readProjectConfigSync(dir).i18n?.client_key;
66
+ }
67
+ function saveI18nClientKey(dir, key) {
68
+ const target = findProjectConfigDir(dir) ?? dir;
69
+ const cfg = readLocalConfig(target);
70
+ writeProjectConfig(target, { ...cfg, i18n: { ...cfg.i18n, client_key: key } });
71
+ }
72
+ function bindProject(dir, projectId, projectName) {
73
+ const existed = existsSync(configPath(dir));
74
+ const cfg = readLocalConfig(dir);
75
+ writeProjectConfig(dir, {
76
+ ...cfg,
77
+ project_id: projectId,
78
+ ...projectName ? { project_name: projectName } : {}
79
+ });
80
+ return { path: configPath(dir), created: !existed };
81
+ }
82
+ var bindProjectSync = bindProject;
83
+ function mergeDetected(dir, detected) {
84
+ const existed = existsSync(configPath(dir));
85
+ const cfg = readLocalConfig(dir);
86
+ writeProjectConfig(dir, {
87
+ ...cfg,
88
+ ...detected.language ? { language: detected.language } : {},
89
+ ...detected.sdk ? { sdk: detected.sdk } : {},
90
+ ...detected.frameworks ? { frameworks: detected.frameworks } : {}
91
+ });
92
+ return { path: configPath(dir), created: !existed };
50
93
  }
51
94
  export {
52
- configPath,
53
- findBoundDir,
95
+ FILENAME,
96
+ SCHEMA_VERSION,
97
+ bindProject,
98
+ bindProjectSync,
99
+ findProjectConfigDir,
54
100
  getBoundProjectId,
55
- resolveConsumerContext
101
+ getBoundProjectIdSync,
102
+ getBoundSdk,
103
+ getI18nClientKey,
104
+ mergeDetected,
105
+ readProjectConfig,
106
+ readProjectConfigSync,
107
+ saveI18nClientKey,
108
+ writeProjectConfig
56
109
  };
110
+ //# sourceMappingURL=node-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node-context/project-config.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\n\n/**\n * The shared `.shipeasy` project-binding file — the single source of truth for\n * both `@shipeasy/cli` and `@shipeasy/mcp`. Lives under `./node-context` because\n * it touches `node:fs`: **never import it from the worker-bundled graph** (the\n * openapi root/`./custom` entries stay fs-free and worker-safe).\n *\n * Model: **one `.shipeasy` per project**. The folder that holds the file IS the\n * project root. Reads walk up from the cwd to the *nearest* file (like git and\n * `.git`), so a subproject's own `.shipeasy` shadows any ancestor and the walk\n * stops there. Writes (`bindProject`, `mergeDetected`) target the LOCAL dir they\n * are given — they never reach up to reuse an ancestor — so each onboarded\n * subproject gets its own file.\n */\nexport const FILENAME = \".shipeasy\";\n\n/** Current `.shipeasy` schema version. Absent in a file → treat as legacy (1). */\nexport const SCHEMA_VERSION = 1;\n\nexport interface ProjectConfig {\n /** Shape version; lets future changes stay backward-compatible. */\n schema_version?: number;\n /**\n * The Shipeasy project this directory pushes to. Required for any mutating\n * CLI/MCP operation. Stops a session logged into project A from silently\n * writing to project B because someone's cwd is wrong.\n */\n project_id?: string;\n /** Human-readable project name — display only, not authoritative. */\n project_name?: string;\n /** Detected language (e.g. `typescript`, `python`) — written by `detect`. */\n language?: string;\n /** Docs SDK handle — the value `shipeasy docs` defaults to when `--sdk` is omitted. */\n sdk?: string;\n /** Detected frameworks (e.g. `[\"nextjs\", \"react\"]`) — written by `detect`. */\n frameworks?: string[];\n i18n?: {\n client_key?: string;\n };\n}\n\nfunction configPath(dir: string): string {\n return join(dir, FILENAME);\n}\n\n/**\n * Walk up from `dir` until a `.shipeasy` file is found, like git does for\n * `.git`. Returns the directory containing the file, or `null` if the search\n * hits the filesystem root without finding one.\n */\nexport function findProjectConfigDir(dir: string): string | null {\n let current = resolve(dir);\n while (true) {\n if (existsSync(join(current, FILENAME))) return current;\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nfunction parse(raw: string): ProjectConfig {\n try {\n return JSON.parse(raw) as ProjectConfig;\n } catch {\n return {};\n }\n}\n\n/** Sync read of the nearest `.shipeasy` (walking up). Empty object if none. */\nexport function readProjectConfigSync(dir: string): ProjectConfig {\n const found = findProjectConfigDir(dir);\n if (!found) return {};\n try {\n return parse(readFileSync(join(found, FILENAME), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** Async read of the nearest `.shipeasy` (walking up). Empty object if none. */\nexport async function readProjectConfig(dir: string): Promise<ProjectConfig> {\n const found = findProjectConfigDir(dir);\n if (!found) return {};\n try {\n return parse(await readFile(join(found, FILENAME), \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** Write `cfg` to `dir/.shipeasy` verbatim (stamping schema_version if absent). */\nexport function writeProjectConfig(dir: string, cfg: ProjectConfig): void {\n const withVersion: ProjectConfig = { schema_version: SCHEMA_VERSION, ...cfg };\n writeFileSync(configPath(dir), JSON.stringify(withVersion, null, 2) + \"\\n\", \"utf8\");\n}\n\n/** Read the LOCAL `.shipeasy` at exactly `dir` (no walk-up). Empty if absent. */\nfunction readLocalConfig(dir: string): ProjectConfig {\n const p = configPath(dir);\n if (!existsSync(p)) return {};\n try {\n return parse(readFileSync(p, \"utf8\"));\n } catch {\n return {};\n }\n}\n\n/** Project id from the nearest `.shipeasy` (sync). */\nexport function getBoundProjectId(dir: string): string | undefined {\n return readProjectConfigSync(dir).project_id;\n}\n/** Alias kept for the MCP call sites. */\nexport const getBoundProjectIdSync = getBoundProjectId;\n\n/** Docs SDK handle from the nearest `.shipeasy` (sync) — the `docs` default. */\nexport function getBoundSdk(dir: string): string | undefined {\n return readProjectConfigSync(dir).sdk;\n}\n\nexport function getI18nClientKey(dir: string): string | undefined {\n return readProjectConfigSync(dir).i18n?.client_key;\n}\n\nexport function saveI18nClientKey(dir: string, key: string): void {\n // Write into the nearest bound dir if one exists; otherwise create in `dir`.\n const target = findProjectConfigDir(dir) ?? dir;\n const cfg = readLocalConfig(target);\n writeProjectConfig(target, { ...cfg, i18n: { ...cfg.i18n, client_key: key } });\n}\n\n/**\n * Bind the LOCAL directory `dir` to `projectId` — writes `dir/.shipeasy`\n * directly (create, or merge over an existing LOCAL file to preserve `sdk` /\n * `i18n` / detection fields). It does NOT walk up to reuse an ancestor file:\n * one project per folder, so binding a subproject never mutates the repo root.\n */\nexport function bindProject(\n dir: string,\n projectId: string,\n projectName?: string,\n): { path: string; created: boolean } {\n const existed = existsSync(configPath(dir));\n const cfg = readLocalConfig(dir);\n writeProjectConfig(dir, {\n ...cfg,\n project_id: projectId,\n ...(projectName ? { project_name: projectName } : {}),\n });\n return { path: configPath(dir), created: !existed };\n}\n/** Alias kept for the MCP call sites. */\nexport const bindProjectSync = bindProject;\n\n/**\n * Non-destructively merge detected language/sdk/frameworks into the LOCAL\n * `dir/.shipeasy` (create it if absent). Never touches `project_id` or any\n * other field — this is what `shipeasy detect` calls per install target so the\n * `docs` default-sdk works even before the project is bound.\n */\nexport function mergeDetected(\n dir: string,\n detected: { language?: string; sdk?: string; frameworks?: string[] },\n): { path: string; created: boolean } {\n const existed = existsSync(configPath(dir));\n const cfg = readLocalConfig(dir);\n writeProjectConfig(dir, {\n ...cfg,\n ...(detected.language ? { language: detected.language } : {}),\n ...(detected.sdk ? { sdk: detected.sdk } : {}),\n ...(detected.frameworks ? { frameworks: detected.frameworks } : {}),\n });\n return { path: configPath(dir), created: !existed };\n}\n"],"mappings":"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","names":[]}