@linzumi/cli 0.0.56-beta → 0.0.57-beta

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +137 -39
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,7 +62,7 @@ Install the CLI or run it with `npx`:
62
62
  ```bash
63
63
  npm install -g @linzumi/cli@latest
64
64
  npx -y @linzumi/cli@latest signup
65
- npx -y @linzumi/cli@0.0.56-beta --version
65
+ npx -y @linzumi/cli@0.0.57-beta --version
66
66
  linzumi --version
67
67
  ```
68
68
 
package/dist/index.js CHANGED
@@ -11028,7 +11028,6 @@ var require_lib3 = __commonJS({
11028
11028
  });
11029
11029
 
11030
11030
  // src/index.ts
11031
- import { randomUUID as randomUUID5 } from "node:crypto";
11032
11031
  import { existsSync as existsSync12, readFileSync as readFileSync13, realpathSync as realpathSync6 } from "node:fs";
11033
11032
  import { homedir as homedir10 } from "node:os";
11034
11033
  import { resolve as resolve10 } from "node:path";
@@ -20800,6 +20799,37 @@ function ensureLocalMachineIdForLinzumiUrl(linzumiUrl, path2 = localConfigPath()
20800
20799
  writeLocalConfigSection({ ...latestConfig, machineId }, path2, linzumiUrl);
20801
20800
  return machineId;
20802
20801
  }
20802
+ function ensureLocalRunnerIdForLinzumiUrl(linzumiUrl, path2 = localConfigPath(), createRunnerId = defaultLocalRunnerId) {
20803
+ if (localConfigScopeKey(linzumiUrl) === prodConfigScope) {
20804
+ return ensureLocalRunnerId(path2, createRunnerId);
20805
+ }
20806
+ const config = readLocalConfigForLinzumiUrl(linzumiUrl, path2);
20807
+ if (config.runnerId !== void 0) {
20808
+ return config.runnerId;
20809
+ }
20810
+ const runnerId = ensureLocalRunnerIdSeed(path2, createRunnerId, linzumiUrl);
20811
+ const latestConfig = readLocalConfigForLinzumiUrl(linzumiUrl, path2);
20812
+ const latestRunnerId = latestConfig.runnerId;
20813
+ if (latestRunnerId !== void 0) {
20814
+ return latestRunnerId;
20815
+ }
20816
+ writeLocalConfigSection({ ...latestConfig, runnerId }, path2, linzumiUrl);
20817
+ return runnerId;
20818
+ }
20819
+ function ensureLocalRunnerId(path2 = localConfigPath(), createRunnerId = defaultLocalRunnerId) {
20820
+ const config = readLocalConfig(path2);
20821
+ if (config.runnerId !== void 0) {
20822
+ return config.runnerId;
20823
+ }
20824
+ const runnerId = ensureLocalRunnerIdSeed(path2, createRunnerId);
20825
+ const latestConfig = readLocalConfig(path2);
20826
+ const latestRunnerId = latestConfig.runnerId;
20827
+ if (latestRunnerId !== void 0) {
20828
+ return latestRunnerId;
20829
+ }
20830
+ writeLocalConfig({ ...latestConfig, runnerId }, path2);
20831
+ return runnerId;
20832
+ }
20803
20833
  function localMachineIdSeedPath(configPath = localConfigPath(), linzumiUrl) {
20804
20834
  if (linzumiUrl !== void 0 && localConfigScopeKey(linzumiUrl) !== prodConfigScope) {
20805
20835
  return join6(
@@ -20809,6 +20839,15 @@ function localMachineIdSeedPath(configPath = localConfigPath(), linzumiUrl) {
20809
20839
  }
20810
20840
  return join6(dirname4(configPath), `${basename4(configPath)}.machine-id`);
20811
20841
  }
20842
+ function localRunnerIdSeedPath(configPath = localConfigPath(), linzumiUrl) {
20843
+ if (linzumiUrl !== void 0 && localConfigScopeKey(linzumiUrl) !== prodConfigScope) {
20844
+ return join6(
20845
+ dirname4(configPath),
20846
+ `${basename4(configPath)}.${localConfigScopeFileStem(linzumiUrl)}.runner-id`
20847
+ );
20848
+ }
20849
+ return join6(dirname4(configPath), `${basename4(configPath)}.runner-id`);
20850
+ }
20812
20851
  function readConfiguredAllowedCwdDetailsForLinzumiUrl(linzumiUrl, path2 = localConfigPath()) {
20813
20852
  return readConfiguredAllowedCwdDetailsFromConfig(
20814
20853
  readLocalConfigForLinzumiUrl(linzumiUrl, path2)
@@ -20971,7 +21010,12 @@ function readLocalConfigSection(path2, linzumiUrl) {
20971
21010
  throw new Error(`invalid Linzumi config section ${scopeKey}: ${path2}`);
20972
21011
  }
20973
21012
  const allowedCwds = uniqueStrings(section.allowedCwds);
20974
- return section.machineId === void 0 ? { version: 1, allowedCwds } : { version: 1, machineId: section.machineId, allowedCwds };
21013
+ return {
21014
+ version: 1,
21015
+ ...section.machineId === void 0 ? {} : { machineId: section.machineId },
21016
+ ...section.runnerId === void 0 ? {} : { runnerId: section.runnerId },
21017
+ allowedCwds
21018
+ };
20975
21019
  }
20976
21020
  function writeLocalConfigSection(config, path2, linzumiUrl) {
20977
21021
  const scopeKey = linzumiUrl === void 0 ? prodConfigScope : localConfigScopeKey(linzumiUrl);
@@ -20988,7 +21032,7 @@ function isConfigPayload(value) {
20988
21032
  return typeof value === "object" && value !== null && value.version === 1 && isConfigSection(value);
20989
21033
  }
20990
21034
  function isConfigSection(value) {
20991
- return typeof value === "object" && value !== null && Array.isArray(value.allowedCwds) && machineIdValid(value.machineId) && value.allowedCwds.every(
21035
+ return typeof value === "object" && value !== null && Array.isArray(value.allowedCwds) && machineIdValid(value.machineId) && runnerIdValid(value.runnerId) && value.allowedCwds.every(
20992
21036
  (cwd) => typeof cwd === "string" && cwd.trim() !== ""
20993
21037
  );
20994
21038
  }
@@ -21030,7 +21074,11 @@ function normalizedConfigSection(config) {
21030
21074
  throw new Error("invalid Linzumi config");
21031
21075
  }
21032
21076
  const allowedCwds = uniqueStrings(config.allowedCwds);
21033
- return config.machineId === void 0 ? { allowedCwds } : { machineId: config.machineId, allowedCwds };
21077
+ return {
21078
+ ...config.machineId === void 0 ? {} : { machineId: config.machineId },
21079
+ ...config.runnerId === void 0 ? {} : { runnerId: config.runnerId },
21080
+ allowedCwds
21081
+ };
21034
21082
  }
21035
21083
  function emptySection() {
21036
21084
  return { allowedCwds: [] };
@@ -21040,6 +21088,9 @@ function machineIdValid(value) {
21040
21088
  value
21041
21089
  );
21042
21090
  }
21091
+ function runnerIdValid(value) {
21092
+ return value === void 0 || typeof value === "string" && /^[a-zA-Z0-9_.-]+$/.test(value);
21093
+ }
21043
21094
  function ensureLocalMachineIdSeed(configPath, createMachineId, linzumiUrl) {
21044
21095
  const seedPath = localMachineIdSeedPath(configPath, linzumiUrl);
21045
21096
  if (existsSync4(seedPath)) {
@@ -21068,6 +21119,34 @@ function ensureLocalMachineIdSeed(configPath, createMachineId, linzumiUrl) {
21068
21119
  unlinkSync(tempPath);
21069
21120
  }
21070
21121
  }
21122
+ function ensureLocalRunnerIdSeed(configPath, createRunnerId, linzumiUrl) {
21123
+ const seedPath = localRunnerIdSeedPath(configPath, linzumiUrl);
21124
+ if (existsSync4(seedPath)) {
21125
+ return readRunnerIdSeed(seedPath);
21126
+ }
21127
+ const runnerId = createRunnerId();
21128
+ if (!runnerIdValid(runnerId)) {
21129
+ throw new Error(`invalid generated Linzumi runner id: ${runnerId}`);
21130
+ }
21131
+ mkdirSync5(dirname4(seedPath), { recursive: true });
21132
+ const tempPath = join6(
21133
+ dirname4(seedPath),
21134
+ `.${basename4(seedPath)}.${process.pid}.${randomUUID2()}.tmp`
21135
+ );
21136
+ writeFileSync4(tempPath, `${runnerId}
21137
+ `, { encoding: "utf8", flag: "wx" });
21138
+ try {
21139
+ linkSync(tempPath, seedPath);
21140
+ return runnerId;
21141
+ } catch (error) {
21142
+ if (isNodeErrorCode(error, "EEXIST")) {
21143
+ return readRunnerIdSeed(seedPath);
21144
+ }
21145
+ throw error;
21146
+ } finally {
21147
+ unlinkSync(tempPath);
21148
+ }
21149
+ }
21071
21150
  function readMachineIdSeed(seedPath) {
21072
21151
  const machineId = readFileSync4(seedPath, "utf8").trim();
21073
21152
  if (!machineIdValid(machineId)) {
@@ -21075,6 +21154,16 @@ function readMachineIdSeed(seedPath) {
21075
21154
  }
21076
21155
  return machineId;
21077
21156
  }
21157
+ function readRunnerIdSeed(seedPath) {
21158
+ const runnerId = readFileSync4(seedPath, "utf8").trim();
21159
+ if (!runnerIdValid(runnerId)) {
21160
+ throw new Error(`invalid Linzumi runner id seed: ${seedPath}`);
21161
+ }
21162
+ return runnerId;
21163
+ }
21164
+ function defaultLocalRunnerId() {
21165
+ return `runner-${randomUUID2()}`;
21166
+ }
21078
21167
  function uniqueStrings(values) {
21079
21168
  return [
21080
21169
  ...new Set(
@@ -21097,7 +21186,7 @@ function realpathOrResolved(pathValue) {
21097
21186
  }
21098
21187
 
21099
21188
  // src/version.ts
21100
- var linzumiCliVersion = "0.0.56-beta";
21189
+ var linzumiCliVersion = "0.0.57-beta";
21101
21190
  var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
21102
21191
 
21103
21192
  // src/runnerLock.ts
@@ -28104,7 +28193,7 @@ async function launchSignupCommanderForCompletedSignup(commanderLauncher, args)
28104
28193
  } catch (error) {
28105
28194
  return {
28106
28195
  status: "failed",
28107
- runnerId: signupCommanderRunnerId(args.workspaceSlug),
28196
+ runnerId: signupCommanderRunnerId(args.serviceUrl),
28108
28197
  restartCommand: signupConnectRestartCommand(args),
28109
28198
  error: error instanceof Error ? error.message : String(error)
28110
28199
  };
@@ -28713,7 +28802,7 @@ async function defaultSignupCommanderLauncher(args) {
28713
28802
  accessToken: args.localRunnerAccessToken,
28714
28803
  expiresInSeconds: args.localRunnerExpiresInSeconds
28715
28804
  });
28716
- const runnerId = signupCommanderRunnerId(args.workspaceSlug);
28805
+ const runnerId = signupCommanderRunnerId(args.serviceUrl);
28717
28806
  const restartCommand = signupConnectRestartCommand(args);
28718
28807
  try {
28719
28808
  const handle = await runLocalCodexRunner(
@@ -28736,13 +28825,13 @@ async function defaultSignupCommanderLauncher(args) {
28736
28825
  };
28737
28826
  }
28738
28827
  }
28739
- function signupCommanderRunnerId(workspaceSlug) {
28740
- return `signup-${workspaceSlug}-commander`;
28828
+ function signupCommanderRunnerId(serviceUrl) {
28829
+ return ensureLocalRunnerIdForLinzumiUrl(serviceUrl);
28741
28830
  }
28742
28831
  function failedSignupCommanderLaunchForMissingCodex(args) {
28743
28832
  return {
28744
28833
  status: "failed",
28745
- runnerId: signupCommanderRunnerId(args.workspaceSlug),
28834
+ runnerId: "commander-not-started",
28746
28835
  restartCommand: signupConnectRestartCommand(args),
28747
28836
  error: "Codex preflight did not resolve a verified Codex command. Install Codex or fix the Codex CLI on PATH, then rerun signup."
28748
28837
  };
@@ -30572,8 +30661,6 @@ function startCommanderDaemon(options) {
30572
30661
  "commander",
30573
30662
  ...options.cwd === void 0 ? [] : [options.cwd],
30574
30663
  ...options.args,
30575
- "--runner-id",
30576
- options.runnerId,
30577
30664
  "--log-file",
30578
30665
  logFile
30579
30666
  ];
@@ -30823,7 +30910,6 @@ var flagDefinitions = /* @__PURE__ */ new Map([
30823
30910
  ["api-url", { kind: "value" }],
30824
30911
  ["linzumi-url", { kind: "value" }],
30825
30912
  ["token", { kind: "value" }],
30826
- ["runner-id", { kind: "value" }],
30827
30913
  ["cwd", { kind: "value" }],
30828
30914
  ["codex-bin", { kind: "value" }],
30829
30915
  ["codex-url", { kind: "value" }],
@@ -31149,7 +31235,8 @@ async function runCommanderDaemonCommand(args) {
31149
31235
  process.stdout.write(commanderDaemonHelpText());
31150
31236
  return;
31151
31237
  }
31152
- const runnerId = required(values, "runner-id");
31238
+ const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
31239
+ const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
31153
31240
  const cwd = cwdArg === void 0 || cwdArg.trim() === "" ? void 0 : resolveUserPath(cwdArg);
31154
31241
  const record = startCommanderDaemon({
31155
31242
  runnerId,
@@ -31173,7 +31260,8 @@ async function runCommanderDaemonCommand(args) {
31173
31260
  }
31174
31261
  case "status": {
31175
31262
  const values = strictFlagValues(rest);
31176
- const runnerId = required(values, "runner-id");
31263
+ const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
31264
+ const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
31177
31265
  const status = commanderDaemonStatus(
31178
31266
  runnerId,
31179
31267
  stringValue4(values, "status-dir")
@@ -31184,7 +31272,8 @@ async function runCommanderDaemonCommand(args) {
31184
31272
  }
31185
31273
  case "wait": {
31186
31274
  const values = strictFlagValues(rest);
31187
- const runnerId = required(values, "runner-id");
31275
+ const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
31276
+ const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
31188
31277
  const timeoutMs = positiveIntegerValue2(values, "timeout-ms") ?? 3e4;
31189
31278
  const result = await waitForCommanderDaemon({
31190
31279
  runnerId,
@@ -31203,7 +31292,8 @@ async function runCommanderDaemonCommand(args) {
31203
31292
  }
31204
31293
  case "stop": {
31205
31294
  const values = strictFlagValues(rest);
31206
- const runnerId = required(values, "runner-id");
31295
+ const kandanUrl = kandanUrlValue(values) ?? defaultLinzumiWebSocketUrl;
31296
+ const runnerId = ensureLocalRunnerIdForLinzumiUrl(kandanUrl);
31207
31297
  const status = stopCommanderDaemon(
31208
31298
  runnerId,
31209
31299
  stringValue4(values, "status-dir")
@@ -31249,7 +31339,8 @@ async function parseStartRunnerArgs(args, deps = {
31249
31339
  fetchStartTarget: fetchLocalRunnerStartTarget,
31250
31340
  validateToken: validateLocalRunnerToken,
31251
31341
  buildDependencyStatus: buildRunnerDependencyStatus,
31252
- resolveEditorRuntime
31342
+ resolveEditorRuntime,
31343
+ ensureRunnerId: ensureLocalRunnerIdForLinzumiUrl
31253
31344
  }) {
31254
31345
  const { cwdArg, flagArgs } = splitStartArgs(args);
31255
31346
  const values = strictFlagValues(flagArgs);
@@ -31321,7 +31412,9 @@ async function parseStartRunnerArgs(args, deps = {
31321
31412
  return {
31322
31413
  kandanUrl,
31323
31414
  token: targetToken,
31324
- runnerId: stringValue4(values, "runner-id") ?? `runner-${randomUUID5()}`,
31415
+ runnerId: (deps.ensureRunnerId ?? ensureLocalRunnerIdForLinzumiUrl)(
31416
+ kandanUrl
31417
+ ),
31325
31418
  workspaceSlug: target.workspaceSlug,
31326
31419
  cwd,
31327
31420
  codexBin,
@@ -31358,7 +31451,8 @@ async function parseStartRunnerArgs(args, deps = {
31358
31451
  async function parseAgentRunnerArgs(args, deps = {
31359
31452
  readTextFile: readAgentTokenTextFile,
31360
31453
  buildDependencyStatus: buildRunnerDependencyStatus,
31361
- resolveEditorRuntime
31454
+ resolveEditorRuntime,
31455
+ ensureRunnerId: ensureLocalRunnerIdForLinzumiUrl
31362
31456
  }) {
31363
31457
  const { cwdArg, flagArgs } = splitStartArgs(args);
31364
31458
  const values = strictFlagValues(flagArgs);
@@ -31423,7 +31517,9 @@ async function parseAgentRunnerArgs(args, deps = {
31423
31517
  return {
31424
31518
  kandanUrl,
31425
31519
  token: tokenFile.commanderToken,
31426
- runnerId: stringValue4(values, "runner-id") ?? `agent-runner-${randomUUID5()}`,
31520
+ runnerId: (deps.ensureRunnerId ?? ensureLocalRunnerIdForLinzumiUrl)(
31521
+ kandanUrl
31522
+ ),
31427
31523
  workspaceSlug: tokenFile.workspaceId,
31428
31524
  cwd,
31429
31525
  codexBin,
@@ -31523,7 +31619,8 @@ async function resolveStartTargetToken(args) {
31523
31619
  async function parseRunnerArgs(args, deps = {
31524
31620
  resolveToken: resolveLocalRunnerToken,
31525
31621
  buildDependencyStatus: buildRunnerDependencyStatus,
31526
- resolveEditorRuntime
31622
+ resolveEditorRuntime,
31623
+ ensureRunnerId: ensureLocalRunnerIdForLinzumiUrl
31527
31624
  }) {
31528
31625
  const values = strictFlagValues(args);
31529
31626
  if (values.get("help") === true) {
@@ -31575,7 +31672,9 @@ async function parseRunnerArgs(args, deps = {
31575
31672
  return {
31576
31673
  kandanUrl,
31577
31674
  token,
31578
- runnerId: stringValue4(values, "runner-id") ?? `runner-${randomUUID5()}`,
31675
+ runnerId: (deps.ensureRunnerId ?? ensureLocalRunnerIdForLinzumiUrl)(
31676
+ kandanUrl
31677
+ ),
31579
31678
  cwd,
31580
31679
  codexBin,
31581
31680
  codexUrl: stringValue4(values, "codex-url"),
@@ -31693,7 +31792,7 @@ function stripDaemonSupervisorFlags(args) {
31693
31792
  }
31694
31793
  const key = arg.startsWith("--") ? arg.slice(2) : void 0;
31695
31794
  const definition = key === void 0 ? void 0 : flagDefinitions.get(key);
31696
- if (key === "runner-id" || key === "log-file" || key === "status-dir") {
31795
+ if (key === "log-file" || key === "status-dir") {
31697
31796
  if (definition?.kind === "value") {
31698
31797
  index += 1;
31699
31798
  }
@@ -31834,8 +31933,8 @@ Usage:
31834
31933
  linzumi done <thread_id> --message <message>
31835
31934
  linzumi init-hello-linzumi-demo-app
31836
31935
  linzumi codex start-new --title <title> --runner <runner_id> --cwd <path> --work-description <text>
31837
- linzumi commander daemon --runner-id <id>
31838
- linzumi commander wait --runner-id <id>
31936
+ linzumi commander daemon
31937
+ linzumi commander wait
31839
31938
  linzumi commander <folder> [options]
31840
31939
  linzumi start <folder> [options]
31841
31940
  linzumi paths list|add|remove [path]
@@ -31879,7 +31978,7 @@ Examples:
31879
31978
  linzumi done thr_abc123 --message "Done: https://github.com/example/repo/pull/1"
31880
31979
  linzumi init-hello-linzumi-demo-app
31881
31980
  linzumi paths add ~/code/my-app
31882
- linzumi commander daemon --runner-id launch-commander
31981
+ linzumi commander daemon
31883
31982
  linzumi start ~/
31884
31983
  linzumi start ~/code/my-app
31885
31984
  linzumi connect --workspace <your-workspace> --launch-tui
@@ -31930,11 +32029,11 @@ function commanderDaemonHelpText() {
31930
32029
  return `Linzumi Commander daemon
31931
32030
 
31932
32031
  Usage:
31933
- linzumi commander daemon --runner-id <id> [options]
31934
- linzumi commander daemon <folder> --runner-id <id> [options]
31935
- linzumi commander status --runner-id <id>
31936
- linzumi commander wait --runner-id <id> [--timeout-ms <ms>]
31937
- linzumi commander stop --runner-id <id>
32032
+ linzumi commander daemon [options]
32033
+ linzumi commander daemon <folder> [options]
32034
+ linzumi commander status [options]
32035
+ linzumi commander wait [--timeout-ms <ms>] [options]
32036
+ linzumi commander stop [options]
31938
32037
 
31939
32038
  What it does:
31940
32039
  Starts the workspace Commander as a detached process, writes a status file,
@@ -31944,7 +32043,7 @@ What it does:
31944
32043
  configured folder.
31945
32044
 
31946
32045
  Options:
31947
- --runner-id <id> Stable Commander id, required
32046
+ --api-url <url> Linzumi API URL used to select the stored scoped runner id
31948
32047
  --status-dir <path> Status directory, default ~/.linzumi/commanders
31949
32048
  --log-file <path> Commander log path, default in the status dir
31950
32049
  --timeout-ms <ms> Wait timeout, default 30000
@@ -32007,9 +32106,9 @@ Usage:
32007
32106
  linzumi commander <folder> [options]
32008
32107
  linzumi commander daemon [options]
32009
32108
  linzumi commander daemon <folder> [options]
32010
- linzumi commander status --runner-id <id>
32011
- linzumi commander wait --runner-id <id>
32012
- linzumi commander stop --runner-id <id>
32109
+ linzumi commander status [options]
32110
+ linzumi commander wait [options]
32111
+ linzumi commander stop [options]
32013
32112
  linzumi agent runner <folder> [options]
32014
32113
 
32015
32114
  What it does:
@@ -32022,7 +32121,6 @@ What it does:
32022
32121
  Options:
32023
32122
  --agent-token-file <path> Agent token cache, default ~/.linzumi/agent-token.json
32024
32123
  --api-url <url> Linzumi API URL. Defaults deterministically from the stored apiUrl.
32025
- --runner-id <id> Stable Commander id
32026
32124
  --codex-bin <path> Codex executable, default codex
32027
32125
  --code-server-bin <path> Custom development code-server executable. By default Linzumi installs the approved editor runtime.
32028
32126
  --listen-user <user> Human whose replies Codex may accept, default owner from claim
@@ -32036,8 +32134,8 @@ Options:
32036
32134
 
32037
32135
  Examples:
32038
32136
  linzumi paths add "$PWD"
32039
- linzumi commander daemon --runner-id hello-world-commander
32040
- linzumi commander ~/code/my-app --api-url http://127.0.0.1:4162 --runner-id local-qa-commander
32137
+ linzumi commander daemon
32138
+ linzumi commander ~/code/my-app --api-url http://127.0.0.1:4162
32041
32139
  `;
32042
32140
  }
32043
32141
  function connectGuideText() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.56-beta",
3
+ "version": "0.0.57-beta",
4
4
  "description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {