@akai-workflow-builder/cli-sdk 0.1.0 → 0.1.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"build-ctx.d.ts","sourceRoot":"","sources":["../../src/ctx/build-ctx.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAc,MAAM,EAAiB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1F;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6GAA6G;IAC7G,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,sGAAsG;IACtG,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AA4CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAsDxD"}
1
+ {"version":3,"file":"build-ctx.d.ts","sourceRoot":"","sources":["../../src/ctx/build-ctx.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAc,MAAM,EAAiB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1F;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6GAA6G;IAC7G,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,sGAAsG;IACtG,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AA4CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAoDxD"}
@@ -71,11 +71,9 @@ export function buildCtx(params) {
71
71
  ...(params.extraSafePathRoots ?? []),
72
72
  ]);
73
73
  const readFile = createReadFile({ safePath, signal: params.signal });
74
- // `ctx.writeFile` is wired only when the host provisioned an output channel
75
- // (outputDir). Tools declaring `options.producesFiles` get a real writer;
76
- // everything else gets a throwing stub so an undeclared write surfaces
77
- // clearly instead of leaking files onto a stray path.
78
- const writeFile = params.outputDir !== undefined
74
+ // Real writer only when the tool opted in (producesFiles) AND the host gave
75
+ // an outputDir enforces the opt-in at the SDK boundary; else a throwing stub.
76
+ const writeFile = tool.options.producesFiles === true && params.outputDir !== undefined
79
77
  ? createWriteFile({ outputDir: params.outputDir })
80
78
  : unavailableWriteFile();
81
79
  return Object.freeze({
@@ -9,11 +9,10 @@ export interface CreateWriteFileOptions {
9
9
  }
10
10
  /**
11
11
  * Build a `ctx.writeFile(name, data)` that stages an output file under the
12
- * invocation's output directory. `name` is a relative path that must stay
13
- * inside `outputDir`: absolute paths and `..` traversal are rejected with
14
- * `AkaiPathError`. Parent directories are created as needed. Pairs with
15
- * `options.producesFiles: true`, which tells the host to provision this
16
- * channel before invoking the tool.
12
+ * invocation's output directory. `name` must resolve inside `outputDir`;
13
+ * absolute paths, `..` escapes, and symlink traversal are rejected with
14
+ * `AkaiPathError` via the same realpath-stabilised check as `ctx.safePath`.
15
+ * Parent directories are created as needed. Pairs with `options.producesFiles`.
17
16
  */
18
17
  export declare function createWriteFile(opts: CreateWriteFileOptions): (name: string, data: Buffer | Uint8Array | string) => Promise<void>;
19
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../src/ctx/write-file.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,sBAAsB,GAC3B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAkBrE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAQrF"}
1
+ {"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../src/ctx/write-file.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,sBAAsB,GAC3B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAoBrE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAQrF"}
@@ -1,16 +1,17 @@
1
1
  import { mkdir, writeFile as fsWriteFile } from 'node:fs/promises';
2
- import { dirname, isAbsolute, relative, resolve } from 'node:path';
2
+ import { dirname, isAbsolute, resolve } from 'node:path';
3
+ import { createSafePath } from './safe-path.js';
3
4
  import { AkaiPathError } from '../errors.js';
4
5
  /**
5
6
  * Build a `ctx.writeFile(name, data)` that stages an output file under the
6
- * invocation's output directory. `name` is a relative path that must stay
7
- * inside `outputDir`: absolute paths and `..` traversal are rejected with
8
- * `AkaiPathError`. Parent directories are created as needed. Pairs with
9
- * `options.producesFiles: true`, which tells the host to provision this
10
- * channel before invoking the tool.
7
+ * invocation's output directory. `name` must resolve inside `outputDir`;
8
+ * absolute paths, `..` escapes, and symlink traversal are rejected with
9
+ * `AkaiPathError` via the same realpath-stabilised check as `ctx.safePath`.
10
+ * Parent directories are created as needed. Pairs with `options.producesFiles`.
11
11
  */
12
12
  export function createWriteFile(opts) {
13
13
  const root = resolve(opts.outputDir);
14
+ const safePath = createSafePath([root]);
14
15
  return async function writeFile(name, data) {
15
16
  if (typeof name !== 'string' || name.length === 0) {
16
17
  throw new Error('ctx.writeFile: name must be a non-empty string');
@@ -18,11 +19,12 @@ export function createWriteFile(opts) {
18
19
  if (isAbsolute(name)) {
19
20
  throw new AkaiPathError(`ctx.writeFile: name must be relative, got absolute path "${name}"`);
20
21
  }
21
- const target = resolve(root, name);
22
- const rel = relative(root, target);
23
- if (rel === '' || rel === '..' || rel.startsWith(`..`) || isAbsolute(rel)) {
24
- throw new AkaiPathError(`ctx.writeFile: name escapes the output directory: "${name}"`);
22
+ const abs = resolve(root, name);
23
+ if (abs === root) {
24
+ throw new AkaiPathError(`ctx.writeFile: name cannot reference the output directory itself: "${name}"`);
25
25
  }
26
+ // realpath-stabilises parents → a symlink can't redirect outside the root.
27
+ const target = safePath(abs);
26
28
  await mkdir(dirname(target), { recursive: true });
27
29
  const bytes = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;
28
30
  await fsWriteFile(target, bytes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akai-workflow-builder/cli-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Authoring SDK for atomic, agent-executable CLIs and tools.",
5
5
  "keywords": [
6
6
  "akai",