orchestrating 0.1.38 → 0.2.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/bin/orch +75 -7
  2. package/package.json +3 -2
package/bin/orch CHANGED
@@ -242,6 +242,50 @@ if (firstArg === "logout") {
242
242
  process.exit(0);
243
243
  }
244
244
 
245
+ // --- Config subcommand: manage API key and settings ---
246
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
247
+
248
+ function loadConfig() {
249
+ try {
250
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
251
+ } catch {
252
+ return {};
253
+ }
254
+ }
255
+
256
+ function saveConfig(data) {
257
+ mkdirSync(CONFIG_DIR, { recursive: true });
258
+ writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2) + "\n");
259
+ }
260
+
261
+ if (firstArg === "config") {
262
+ const configArgs = process.argv.slice(3);
263
+ if (configArgs[0] === "set" && configArgs[1] === "api-key" && configArgs[2]) {
264
+ const config = loadConfig();
265
+ config.anthropic_api_key = configArgs[2];
266
+ saveConfig(config);
267
+ console.log("Anthropic API key saved. Claude Code will use this key automatically.");
268
+ } else if (configArgs[0] === "get" && configArgs[1] === "api-key") {
269
+ const config = loadConfig();
270
+ if (config.anthropic_api_key) {
271
+ console.log(`API key: ${config.anthropic_api_key.slice(0, 10)}…${config.anthropic_api_key.slice(-4)}`);
272
+ } else {
273
+ console.log("No API key configured. Run: orch config set api-key <your-anthropic-key>");
274
+ }
275
+ } else if (configArgs[0] === "unset" && configArgs[1] === "api-key") {
276
+ const config = loadConfig();
277
+ delete config.anthropic_api_key;
278
+ saveConfig(config);
279
+ console.log("API key removed.");
280
+ } else {
281
+ console.error("Usage:");
282
+ console.error(" orch config set api-key <key> Save your Anthropic API key");
283
+ console.error(" orch config get api-key Show stored API key");
284
+ console.error(" orch config unset api-key Remove stored API key");
285
+ }
286
+ process.exit(0);
287
+ }
288
+
245
289
  if (firstArg === "daemon") {
246
290
  const daemonArgs = process.argv.slice(3);
247
291
  const pidFile = path.join(os.homedir(), ".orch-daemon.pid");
@@ -740,6 +784,7 @@ while (i < args.length) {
740
784
  console.error("Usage: orch [-l label] [-y] <command> [args...]");
741
785
  console.error(" orch login — Authenticate with orchestrat.ing");
742
786
  console.error(" orch logout — Clear stored credentials");
787
+ console.error(" orch config — Manage settings (API keys, etc.)");
743
788
  console.error(" orch daemon — Run background daemon for remote session launching");
744
789
  console.error("");
745
790
  console.error(" -l <label> Optional human-readable session label");
@@ -763,6 +808,10 @@ while (i < args.length) {
763
808
  console.error(" orch daemon -b");
764
809
  console.error(" orch daemon --enable");
765
810
  console.error("");
811
+ console.error("Setup (one-time):");
812
+ console.error(" orch login");
813
+ console.error(' orch config set api-key sk-ant-... # Your Anthropic API key');
814
+ console.error("");
766
815
  console.error("Environment:");
767
816
  console.error(" ORC_URL WebSocket server URL (default: wss://api.orchestrat.ing/ws)");
768
817
  console.error(" ORC_TOKEN Auth token (overrides stored credentials)");
@@ -779,7 +828,19 @@ if (commandArgs.length === 0) {
779
828
  process.exit(1);
780
829
  }
781
830
 
782
- const command = commandArgs[0];
831
+ // Resolve claude binary: prefer bundled @anthropic-ai/claude-code, fall back to system PATH
832
+ let command = commandArgs[0];
833
+ let claudeBundled = false;
834
+ if (command === "claude") {
835
+ try {
836
+ // The npm package has a bin entry that npm links globally; for local use, resolve directly
837
+ const bundledBin = path.join(__dirname, "..", "node_modules", ".bin", "claude");
838
+ if (existsSync(bundledBin)) {
839
+ command = bundledBin;
840
+ claudeBundled = true;
841
+ }
842
+ } catch {}
843
+ }
783
844
  const spawnArgs = commandArgs.slice(1);
784
845
  let sessionId;
785
846
  const hostname = os.hostname().replace(/\.(lan|local|home|internal)$/i, "");
@@ -830,6 +891,7 @@ const eventHistory = []; // all agent events for replay on reconnect
830
891
  function sendToServer(msg) {
831
892
  // Track agent events for reconnect replay
832
893
  if (msg.type === "agent_event" && msg.event) {
894
+ if (!msg.event.ts) msg.event.ts = Date.now();
833
895
  eventHistory.push(msg.event);
834
896
  while (eventHistory.length > MAX_EVENT_HISTORY) {
835
897
  eventHistory.shift();
@@ -942,11 +1004,15 @@ if (adapter) {
942
1004
  // Confirmation-type tools — these need "yes" response, not permission grants
943
1005
  const CONFIRMATION_TOOLS = new Set(["ExitPlanMode", "EnterPlanMode"]);
944
1006
 
945
- // Build clean env for child processes (no nesting detection, no leaked keys)
1007
+ // Build clean env for child processes
1008
+ // Inject stored Anthropic API key if available (so users don't need separate `claude login`)
946
1009
  const childEnv = (() => {
947
1010
  const e = { ...process.env };
948
1011
  delete e.CLAUDECODE;
949
- delete e.ANTHROPIC_API_KEY;
1012
+ const config = loadConfig();
1013
+ if (config.anthropic_api_key && !e.ANTHROPIC_API_KEY) {
1014
+ e.ANTHROPIC_API_KEY = config.anthropic_api_key;
1015
+ }
950
1016
  return e;
951
1017
  })();
952
1018
 
@@ -1083,20 +1149,21 @@ if (adapter) {
1083
1149
  for (const line of latest.split("\n").filter(Boolean)) {
1084
1150
  try {
1085
1151
  const record = JSON.parse(line);
1152
+ const ts = record.timestamp ? new Date(record.timestamp).getTime() : undefined;
1086
1153
  if (record.type === "user" && record.message?.content) {
1087
1154
  const text = typeof record.message.content === "string"
1088
1155
  ? record.message.content
1089
1156
  : record.message.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
1090
1157
  if (text) {
1091
- historyEvents.push({ kind: "user_message", text });
1158
+ historyEvents.push({ kind: "user_message", text, ...(ts ? { ts } : {}) });
1092
1159
  }
1093
1160
  } else if (record.message?.role === "assistant" && record.message?.content) {
1094
1161
  const msgId = `hist-${msgCounter++}`;
1095
- historyEvents.push({ kind: "message_start", messageId: msgId });
1162
+ historyEvents.push({ kind: "message_start", messageId: msgId, ...(ts ? { ts } : {}) });
1096
1163
  for (const block of record.message.content) {
1097
1164
  if (block.type === "text" && block.text) {
1098
1165
  const blockId = `hist-text-${msgCounter++}`;
1099
- historyEvents.push({ kind: "text_delta", blockId, text: block.text });
1166
+ historyEvents.push({ kind: "text_delta", blockId, text: block.text, ...(ts ? { ts } : {}) });
1100
1167
  } else if (block.type === "tool_use") {
1101
1168
  const blockId = `hist-tool-${msgCounter++}`;
1102
1169
  historyEvents.push({
@@ -1104,10 +1171,11 @@ if (adapter) {
1104
1171
  toolName: block.name || "unknown",
1105
1172
  toolId: block.id || blockId,
1106
1173
  input: typeof block.input === "string" ? block.input : JSON.stringify(block.input || {}),
1174
+ ...(ts ? { ts } : {}),
1107
1175
  });
1108
1176
  }
1109
1177
  }
1110
- historyEvents.push({ kind: "message_end", messageId: msgId });
1178
+ historyEvents.push({ kind: "message_end", messageId: msgId, ...(ts ? { ts } : {}) });
1111
1179
  }
1112
1180
  } catch {}
1113
1181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrating",
3
- "version": "0.1.38",
3
+ "version": "0.2.0",
4
4
  "description": "Stream terminal sessions to the orchestrat.ing dashboard",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,6 +30,7 @@
30
30
  "dashboard"
31
31
  ],
32
32
  "dependencies": {
33
- "ws": "^8.18.0"
33
+ "ws": "^8.18.0",
34
+ "@anthropic-ai/claude-code": "^2.1.0"
34
35
  }
35
36
  }