apcore-cli 0.8.0 → 0.8.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to apcore-cli (TypeScript SDK) will be documented in this fi
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.8.1] - 2026-05-09
9
+
10
+ ### Fixed
11
+
12
+ - **Init-time deadlock under Bun (`src/security/sandbox.ts`).** The
13
+ sandbox runner's 5 `await import('node:child_process|os|path|fs')`
14
+ calls were hoisted to static `import` statements at the top of
15
+ `sandbox.ts`. The dynamic-import pattern was a holdover from when
16
+ apcore-cli targeted both Node and browser; the CLI is Node-only by
17
+ nature (`#!/usr/bin/env node`, `process.argv[1]` re-exec, child-
18
+ process spawning), so deferring the imports added no value and
19
+ contributed to the Bun deadlock chain when the CLI was loaded via
20
+ `bun run dist/bin/apcore-cli.js`. Verified end-to-end on Bun 1.3.13:
21
+ `--version` returns in 108 ms (was: indefinite hang on Bun 1.2.x).
22
+ No public API change.
23
+ - **C-SNAKE/1 — schema kwargs forwarded under commander's camelCase keys instead of the schema's snake_case property names** (`src/main.ts:985-998`). Commander stores parsed flag values under camelCased attribute names (`--has-solution` → `options.hasSolution`); the action handler previously passed `Object.entries(options)` straight into `schemaKwargs`, so modules reading `input["has_solution"]` always saw `undefined`. Single-word flags (`--module`, `--page`) coincidentally worked because their camelCase form matches the schema name. Multi-word fields (`has_solution`, `sort_by`, `sort_order`) were silently dropped. The fix iterates `schemaOptions` and writes each value back under its original `propName`, matching Python click's auto-derived parameter name and Rust clap's explicit `Arg::new(prop_name)` semantics. Cross-SDK parity restored.
24
+ - **C-SNAKE/2 — boolean `--flag/--no-flag` pair was registered as a single comma-combined commander option** (`src/main.ts:957-975`). The schema-parser produced `flags: "--<flag>, --no-<flag>"` and the registration loop forwarded that string to `cmd.option(...)`. Commander does not parse the comma form the way Python click's `--flag/--no-flag` does — it routes both forms to the negated attribute and stores `false` for both, so `--has-solution` did not flip the value to `true`. Boolean schema flags now register as two separate `Option`s (`--<flag>` carrying the schema default + help, plus a hidden `--no-<flag>` companion); commander's auto-negation routes both to the same camelCase attribute and applies the correct value.
25
+
26
+ ### Added
27
+
28
+ - **`tests/conformance/snake-case-kwargs.test.ts`** — runs the cross-language Algorithm C-SNAKE fixture (`apcore-cli/conformance/fixtures/snake-case-kwargs/cases.json`) against `buildModuleCommand`. Five cases cover positive flag, negation, default fallback, snake_case string flags, and a multi-flag combination. The same fixture is consumed verbatim by the Python and Rust SDK runners.
29
+
30
+
8
31
  ## [0.8.0] - 2026-05-08
9
32
 
10
33
  ### Security
@@ -157,6 +157,10 @@ __export(sandbox_exports, {
157
157
  Sandbox: () => Sandbox,
158
158
  runSandboxRunner: () => runSandboxRunner
159
159
  });
160
+ import { spawn } from "child_process";
161
+ import { mkdtempSync, rmSync } from "fs";
162
+ import { tmpdir } from "os";
163
+ import { join, resolve as resolvePath } from "path";
160
164
  async function runSandboxRunner(moduleId) {
161
165
  const extensionsRoot = process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
162
166
  const apcore = await import("apcore-js").catch(() => {
@@ -260,13 +264,8 @@ var init_sandbox = __esm({
260
264
  return this._sandboxedExecute(moduleId, inputData);
261
265
  }
262
266
  async _sandboxedExecute(moduleId, inputData) {
263
- const { spawn } = await import("child_process");
264
- const { tmpdir } = await import("os");
265
- const { join: join4 } = await import("path");
266
- const { mkdtempSync, rmSync } = await import("fs");
267
- const tmpDir = mkdtempSync(join4(tmpdir(), "apcore_sandbox_"));
267
+ const tmpDir = mkdtempSync(join(tmpdir(), "apcore_sandbox_"));
268
268
  const env = buildSandboxEnv(tmpDir);
269
- const { resolve: resolvePath } = await import("path");
270
269
  if (this.extensionsRoot !== null) {
271
270
  env.APCORE_EXTENSIONS_ROOT = resolvePath(this.extensionsRoot);
272
271
  } else if (env.APCORE_EXTENSIONS_ROOT) {
@@ -4156,7 +4155,15 @@ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdNam
4156
4155
  cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
4157
4156
  }
4158
4157
  for (const opt of schemaOptions) {
4159
- if (opt.parseArg) {
4158
+ if (opt.isBooleanFlag) {
4159
+ const flagBase = opt.name.replace(/_/g, "-");
4160
+ cmd.addOption(
4161
+ new Option4(`--${flagBase}`, opt.description).default(
4162
+ opt.defaultValue
4163
+ )
4164
+ );
4165
+ cmd.addOption(new Option4(`--no-${flagBase}`).hideHelp());
4166
+ } else if (opt.parseArg) {
4160
4167
  cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
4161
4168
  } else {
4162
4169
  cmd.option(opt.flags, opt.description, opt.defaultValue);
@@ -4180,24 +4187,12 @@ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdNam
4180
4187
  );
4181
4188
  const approvalToken = options.approvalToken;
4182
4189
  const schemaKwargs = {};
4183
- const builtinKeys = /* @__PURE__ */ new Set([
4184
- "input",
4185
- "yes",
4186
- "largeInput",
4187
- "format",
4188
- "fields",
4189
- "sandbox",
4190
- "verbose",
4191
- "dryRun",
4192
- "trace",
4193
- "stream",
4194
- "strategy",
4195
- "approvalTimeout",
4196
- "approvalToken"
4197
- ]);
4198
- for (const [k, v] of Object.entries(options)) {
4199
- if (!builtinKeys.has(k)) {
4200
- schemaKwargs[k] = v;
4190
+ for (const opt of schemaOptions) {
4191
+ const commanderKey = opt.name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
4192
+ if (commanderKey in options) {
4193
+ schemaKwargs[opt.name] = options[commanderKey];
4194
+ } else if (opt.name in options) {
4195
+ schemaKwargs[opt.name] = options[opt.name];
4201
4196
  }
4202
4197
  }
4203
4198
  let merged = {};