@4pm/cli 0.8.3 → 0.9.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 (2) hide show
  1. package/dist/index.js +148 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28276,13 +28276,13 @@ function defaultProfileConfig() {
28276
28276
  return {
28277
28277
  aiCli: "claude",
28278
28278
  // Lists so failover across profiles is discoverable (ADR-0057) — add more objects.
28279
- // `args` are the full per-CLI defaults (claude stream-json for metering — ADR-0072).
28279
+ // `args` here are EXTRA args only (empty by default) — the token-metering flags
28280
+ // (claude stream-json / codex `exec --json`) are hardcoded in source and always
28281
+ // injected (ADR-0158, ADR-0072), so they cannot be dropped by editing this file.
28280
28282
  // `model` = the CLI's `--model` flag; default "sonnet" — the balanced mid-price tier
28281
28283
  // (cheaper than opus, more capable than haiku). "" ⇒ the CLI's own default model.
28282
- claudeHome: [
28283
- { profile: ".claude", args: ["-p", "--output-format", "stream-json", "--verbose"], model: "sonnet" }
28284
- ],
28285
- codexHome: [{ profile: ".codex", args: ["exec"], model: "" }],
28284
+ claudeHome: [{ profile: ".claude", args: [], model: "sonnet" }],
28285
+ codexHome: [{ profile: ".codex", args: [], model: "" }],
28286
28286
  antigravityHome: [],
28287
28287
  autoUpdate: true,
28288
28288
  commandHistoryUploadMinutes: 10,
@@ -28585,6 +28585,7 @@ async function updateViaDownload(tarballUrl, checksum, signature) {
28585
28585
  });
28586
28586
  }
28587
28587
  async function updateToLatest(serverUrl) {
28588
+ if (CLI_VERSION.startsWith("0.0.0")) return { action: "dev-build", version: CLI_VERSION };
28588
28589
  const meta = await fetchCliVersion(serverUrl);
28589
28590
  if (compareSemver(CLI_VERSION, meta.latest) >= 0) {
28590
28591
  return { action: "already-latest", version: CLI_VERSION };
@@ -28819,7 +28820,10 @@ var PermissionCode = {
28819
28820
  INTEGRATION_PAGE_WRITE: "integration.page_write",
28820
28821
  // community "4rum" news/forum (MEMO) — every role reads/posts (org-scoped)
28821
28822
  COMMUNITY_READ: "community.read",
28822
- COMMUNITY_POST: "community.post"
28823
+ COMMUNITY_POST: "community.post",
28824
+ // support & AI help (ADR-0167/0168) — self-scoped baseline for every role
28825
+ SUPPORT_USE: "support.use",
28826
+ HELP_USE: "help.use"
28823
28827
  };
28824
28828
  var ALL_PERMISSION_CODES = Object.values(PermissionCode);
28825
28829
  var GrantSubjectType = {
@@ -28838,7 +28842,10 @@ var COMMON = [
28838
28842
  P.SEARCH_QUERY,
28839
28843
  // community 4rum — read + post (org-scoped — MEMO)
28840
28844
  P.COMMUNITY_READ,
28841
- P.COMMUNITY_POST
28845
+ P.COMMUNITY_POST,
28846
+ // file/read own support tickets + ask the AI Help agent (ADR-0167/0168)
28847
+ P.SUPPORT_USE,
28848
+ P.HELP_USE
28842
28849
  ];
28843
28850
  var ROLE_DEFAULT_PERMISSIONS = {
28844
28851
  // ADMIN: full permissions
@@ -33196,7 +33203,10 @@ var registerRequestSchema = external_exports.object({
33196
33203
  phone: phoneSchema,
33197
33204
  ipAllowlist: ipAllowlistSchema.optional(),
33198
33205
  planCode: external_exports.string().min(1).max(50).optional(),
33199
- provider: paymentProviderSchema.optional()
33206
+ provider: paymentProviderSchema.optional(),
33207
+ // One-time token from an SSO "prefill" round-trip (ADR-0161): a verified provider profile
33208
+ // whose email must match `email`; on success the identity is linked to the new account.
33209
+ ssoToken: hexTokenSchema.optional()
33200
33210
  });
33201
33211
  var mailTokenRequestSchema = external_exports.object({
33202
33212
  token: hexTokenSchema
@@ -33405,6 +33415,7 @@ var updateMemoItemSchema = external_exports.object({
33405
33415
  }).refine((v) => v.content !== void 0 || v.done !== void 0, {
33406
33416
  message: "content or done required"
33407
33417
  });
33418
+ var HTTP_JSON_BODY_LIMIT_BYTES = 2 * 1048576;
33408
33419
  var putSpecDraftRequestSchema = external_exports.object({
33409
33420
  spec: external_exports.record(external_exports.unknown()),
33410
33421
  specVersion: external_exports.number().int().positive().optional()
@@ -33633,7 +33644,16 @@ var COMMAND_MAX_LEN = 8e3;
33633
33644
  var AI_PROMPT_MAX_LEN = 2e5;
33634
33645
  var dispatchCommandRequestSchema = external_exports.object({
33635
33646
  projectId: external_exports.string().uuid(),
33636
- machineLinkId: external_exports.string().uuid(),
33647
+ // Target cli. Required unless `pick` is set — with `pick:"idle"` the server chooses the
33648
+ // target from the project's pool, so the client omits it (ADR-0171).
33649
+ machineLinkId: external_exports.string().uuid().optional(),
33650
+ /**
33651
+ * Server-side idle-cli pick (ADR-0171): `"idle"` ⇒ omit `machineLinkId` and let the server
33652
+ * select + atomically claim an idle cli from the project's pool (machine-users + org
33653
+ * orchestrator). Used by the wizard's AI ops so parallel suggests spread across the pool
33654
+ * without double-booking a busy cli; no idle cli ⇒ `ALL_CLIS_BUSY`.
33655
+ */
33656
+ pick: external_exports.enum(["idle"]).optional(),
33637
33657
  // In AI mode `command` carries the raw prompt (can be long — the spec-assist prompts
33638
33658
  // embed the whole self-describing spec envelope, ADR-0106), else an executable name.
33639
33659
  command: external_exports.string().min(1).max(AI_PROMPT_MAX_LEN),
@@ -33656,6 +33676,13 @@ var dispatchCommandRequestSchema = external_exports.object({
33656
33676
  */
33657
33677
  gitOp: external_exports.enum(["read", "write"]).optional()
33658
33678
  }).superRefine((v, ctx) => {
33679
+ if (v.machineLinkId == null === (v.pick == null)) {
33680
+ ctx.addIssue({
33681
+ code: external_exports.ZodIssueCode.custom,
33682
+ path: ["machineLinkId"],
33683
+ message: "Provide exactly one of machineLinkId or pick."
33684
+ });
33685
+ }
33659
33686
  if (!v.ai && v.command.length > COMMAND_MAX_LEN) {
33660
33687
  ctx.addIssue({
33661
33688
  code: external_exports.ZodIssueCode.too_big,
@@ -33866,6 +33893,62 @@ var createCommunityPostSchema = external_exports.object({
33866
33893
  authorName: external_exports.string().max(100).optional()
33867
33894
  });
33868
33895
 
33896
+ // ../../22-packages/11-shared/31-dto/src/support.ts
33897
+ var SUPPORT_CATEGORIES = [
33898
+ "bug",
33899
+ "billing",
33900
+ "how_to",
33901
+ "account",
33902
+ "other"
33903
+ ];
33904
+ var SUPPORT_STATUSES = [
33905
+ "open",
33906
+ "in_progress",
33907
+ "resolved",
33908
+ "closed"
33909
+ ];
33910
+ var supportContextSchema = external_exports.object({
33911
+ route: external_exports.string().max(512).optional(),
33912
+ orgId: external_exports.string().max(64).optional(),
33913
+ projectId: external_exports.string().max(64).optional(),
33914
+ appVersion: external_exports.string().max(64).optional(),
33915
+ locale: external_exports.string().max(16).optional()
33916
+ }).optional();
33917
+ var createSupportTicketRequestSchema = external_exports.object({
33918
+ category: external_exports.enum(SUPPORT_CATEGORIES),
33919
+ subject: external_exports.string().min(1).max(160),
33920
+ body: external_exports.string().min(1).max(1e4),
33921
+ attachments: external_exports.array(external_exports.string().max(512)).max(10).optional(),
33922
+ context: supportContextSchema
33923
+ });
33924
+ var listSupportTicketsQuerySchema = baseRequestSchema.extend({
33925
+ status: external_exports.enum(SUPPORT_STATUSES).optional(),
33926
+ category: external_exports.enum(SUPPORT_CATEGORIES).optional()
33927
+ });
33928
+ var replySupportTicketRequestSchema = external_exports.object({
33929
+ body: external_exports.string().min(1).max(1e4),
33930
+ attachments: external_exports.array(external_exports.string().max(512)).max(10).optional()
33931
+ });
33932
+ var adminListSupportTicketsQuerySchema = baseRequestSchema.extend({
33933
+ status: external_exports.enum(SUPPORT_STATUSES).optional(),
33934
+ category: external_exports.enum(SUPPORT_CATEGORIES).optional(),
33935
+ orgId: external_exports.string().uuid().optional()
33936
+ });
33937
+ var adminReplySupportTicketRequestSchema = external_exports.object({
33938
+ body: external_exports.string().min(1).max(1e4),
33939
+ status: external_exports.enum(SUPPORT_STATUSES).optional(),
33940
+ assign: external_exports.boolean().optional(),
33941
+ // the acting platform admin's GitHub id (author trace), filled by admin-server.
33942
+ by: external_exports.string().max(64).optional()
33943
+ });
33944
+
33945
+ // ../../22-packages/11-shared/31-dto/src/help.ts
33946
+ var askHelpRequestSchema = external_exports.object({
33947
+ question: external_exports.string().min(1).max(2e3),
33948
+ // continue an existing conversation; omit ⇒ a new thread.
33949
+ threadId: external_exports.string().uuid().optional()
33950
+ });
33951
+
33869
33952
  // ../../22-packages/21-protocol/21-websocket/src/channels/index.ts
33870
33953
  var WsChannels = {
33871
33954
  ...WsChannel,
@@ -34336,18 +34419,11 @@ function assistantText(ev) {
34336
34419
  }
34337
34420
  function createAiStreamParser(cli) {
34338
34421
  const isClaude = cli.includes("claude");
34422
+ const isCodex = cli.includes("codex");
34423
+ const isJson = isClaude || isCodex;
34339
34424
  let buffer = "";
34340
34425
  const acc = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
34341
- function handleLine(line) {
34342
- const trimmed = line.trim();
34343
- if (!isClaude || !trimmed.startsWith("{")) return null;
34344
- let ev;
34345
- try {
34346
- ev = JSON.parse(trimmed);
34347
- } catch {
34348
- return null;
34349
- }
34350
- if (typeof ev.type !== "string") return null;
34426
+ function handleClaude(ev) {
34351
34427
  if (ev.type === "result" && ev.usage) {
34352
34428
  acc.input = ev.usage.input_tokens ?? 0;
34353
34429
  acc.output = ev.usage.output_tokens ?? 0;
@@ -34364,6 +34440,34 @@ function createAiStreamParser(cli) {
34364
34440
  }
34365
34441
  return "";
34366
34442
  }
34443
+ function handleCodex(ev) {
34444
+ if (ev.type === "turn.completed" && ev.usage) {
34445
+ acc.input = ev.usage.input_tokens ?? 0;
34446
+ acc.output = (ev.usage.output_tokens ?? 0) + (ev.usage.reasoning_output_tokens ?? 0);
34447
+ acc.cacheRead = ev.usage.cached_input_tokens ?? 0;
34448
+ acc.cacheCreation = 0;
34449
+ acc.tokens = acc.input + acc.output;
34450
+ return "";
34451
+ }
34452
+ if (ev.type === "item.completed" && ev.item?.type === "agent_message") {
34453
+ const text = ev.item.text;
34454
+ return text ? `${text}
34455
+ ` : "";
34456
+ }
34457
+ return "";
34458
+ }
34459
+ function handleLine(line) {
34460
+ const trimmed = line.trim();
34461
+ if (!isJson || !trimmed.startsWith("{")) return null;
34462
+ let ev;
34463
+ try {
34464
+ ev = JSON.parse(trimmed);
34465
+ } catch {
34466
+ return null;
34467
+ }
34468
+ if (typeof ev.type !== "string") return null;
34469
+ return isCodex ? handleCodex(ev) : handleClaude(ev);
34470
+ }
34367
34471
  return {
34368
34472
  push(raw) {
34369
34473
  buffer += raw;
@@ -34476,10 +34580,16 @@ function setWorkingProfile(profileDir2, cmd, dir2) {
34476
34580
  import { readFileSync as readFileSync8 } from "fs";
34477
34581
  import { homedir as homedir3 } from "os";
34478
34582
  import { basename, isAbsolute, join as join8 } from "path";
34479
- var DEFAULT_AI_ARGS = {
34583
+ var REQUIRED_AI_ARGS = {
34480
34584
  claude: ["-p", "--output-format", "stream-json", "--verbose"],
34481
- codex: ["exec"]
34585
+ codex: ["exec", "--json"],
34586
+ antigravity: []
34482
34587
  };
34588
+ function requiredArgs(cmd) {
34589
+ if (cmd.includes("claude")) return REQUIRED_AI_ARGS.claude;
34590
+ if (cmd.includes("codex")) return REQUIRED_AI_ARGS.codex;
34591
+ return REQUIRED_AI_ARGS.antigravity;
34592
+ }
34483
34593
  var DEFAULT_AI_CLI = "claude";
34484
34594
  function resolveHomePath(value) {
34485
34595
  if (value.startsWith("~")) return join8(homedir3(), value.slice(1).replace(/^[/\\]/, ""));
@@ -34491,10 +34601,10 @@ function profileDirs(profiles) {
34491
34601
  return profiles.map((p) => p.profile).filter((v) => v && v.trim()).map(resolveHomePath);
34492
34602
  }
34493
34603
  function buildRunArgs(profile, cmd, prompt) {
34494
- const pre = profile.args ?? DEFAULT_AI_ARGS[cmd] ?? [];
34604
+ const extras = profile.args ?? [];
34495
34605
  const model = profile.model?.trim();
34496
34606
  const modelArgs = model ? ["--model", model] : [];
34497
- return [...pre, ...modelArgs, prompt];
34607
+ return [...requiredArgs(cmd), ...extras, ...modelArgs, prompt];
34498
34608
  }
34499
34609
  function claudeHomeDirs(config) {
34500
34610
  const dirs = profileDirs(config.claudeHome);
@@ -44283,17 +44393,31 @@ async function runUnlink(profileDir2, profileName) {
44283
44393
  }
44284
44394
 
44285
44395
  // src/commands/update.ts
44396
+ function isConnectionError(err) {
44397
+ return err instanceof TypeError && err.message === "fetch failed";
44398
+ }
44286
44399
  async function runUpdate(profileDir2, profileName, serverUrlFlag) {
44287
44400
  const serverUrl = (serverUrlFlag || readCredential(profileDir2, profileName)?.serverUrl || process.env.FOURPM_SERVER || "http://localhost:42001").replace(/\/$/, "");
44288
44401
  let result;
44289
44402
  try {
44290
44403
  result = await updateToLatest(serverUrl);
44291
44404
  } catch (err) {
44292
- console.error(`Could not check for updates: ${err}`);
44405
+ if (isConnectionError(err)) {
44406
+ console.error(
44407
+ `Could not reach the server at ${serverUrl} \u2014 is it running? Set the server with \`--server <url>\` or the FOURPM_SERVER env var.`
44408
+ );
44409
+ } else {
44410
+ console.error(`Could not check for updates: ${err}`);
44411
+ }
44293
44412
  process.exitCode = 1;
44294
44413
  return;
44295
44414
  }
44296
44415
  switch (result.action) {
44416
+ case "dev-build":
44417
+ console.log(
44418
+ `Running a local dev build (${result.version}) \u2014 update it by rebuilding the repo (\`pnpm --filter @4pm/cli build\`), not \`4pm update\`.`
44419
+ );
44420
+ break;
44297
44421
  case "already-latest":
44298
44422
  console.log(`Already up to date (${result.version}).`);
44299
44423
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4pm/cli",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "4PM CLI — drives Claude/Codex CLIs on AI worker machines (Node + TypeScript)",
6
6
  "type": "module",