@prismer/sdk 2.0.7 → 2.0.8

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/dist/cli.js CHANGED
@@ -163,7 +163,7 @@ var RealtimeWSClient = class extends TypedEmitter {
163
163
  if (this._state === "connected" || this._state === "connecting") return;
164
164
  this._state = "connecting";
165
165
  this.intentionalClose = false;
166
- return new Promise((resolve5, reject) => {
166
+ return new Promise((resolve6, reject) => {
167
167
  try {
168
168
  this.ws = new this.WS(this.wsUrl);
169
169
  } catch (err) {
@@ -185,7 +185,7 @@ var RealtimeWSClient = class extends TypedEmitter {
185
185
  this.emit("connected", void 0);
186
186
  this.ws.removeEventListener("message", onFirstMessage);
187
187
  this.ws.addEventListener("message", this.handleMessage);
188
- resolve5();
188
+ resolve6();
189
189
  }
190
190
  } catch (_) {
191
191
  }
@@ -257,12 +257,12 @@ var RealtimeWSClient = class extends TypedEmitter {
257
257
  }
258
258
  ping() {
259
259
  const requestId = `ping-${++this.pingCounter}`;
260
- return new Promise((resolve5, reject) => {
260
+ return new Promise((resolve6, reject) => {
261
261
  const timer = setTimeout(() => {
262
262
  this.pendingPings.delete(requestId);
263
263
  reject(new Error("Ping timeout"));
264
264
  }, 1e4);
265
- this.pendingPings.set(requestId, { resolve: resolve5, timer });
265
+ this.pendingPings.set(requestId, { resolve: resolve6, timer });
266
266
  this.sendRaw({ type: "ping", payload: { requestId } });
267
267
  });
268
268
  }
@@ -3868,6 +3868,19 @@ var FilesClient = class {
3868
3868
  */
3869
3869
  async sendFile(conversationId, input, opts) {
3870
3870
  const uploaded = await this.upload(input, opts);
3871
+ const outboxDir = process.env.PRISMER_OUTBOX_DIR;
3872
+ if (outboxDir) {
3873
+ try {
3874
+ const fsMod = await import("fs");
3875
+ const pathMod = await import("path");
3876
+ const srcPath = typeof input === "string" ? input : input.path;
3877
+ if (srcPath && typeof srcPath === "string") {
3878
+ await fsMod.promises.mkdir(outboxDir, { recursive: true });
3879
+ fsMod.cpSync(srcPath, pathMod.join(outboxDir, pathMod.basename(srcPath)));
3880
+ }
3881
+ } catch {
3882
+ }
3883
+ }
3871
3884
  const msgRes = await this._r("POST", `/api/im/messages/${conversationId}`, {
3872
3885
  content: opts?.content || uploaded.fileName,
3873
3886
  type: "file",
@@ -4184,6 +4197,7 @@ var PrismerClient = class {
4184
4197
  this.timeout = config.timeout || 3e4;
4185
4198
  this.fetchFn = config.fetch || fetch;
4186
4199
  this.imAgent = config.imAgent;
4200
+ this.imWorkspace = config.imWorkspace;
4187
4201
  if (config.identity) {
4188
4202
  if (config.identity === "auto" && this.apiKey) {
4189
4203
  this._identityReady = import_aip_sdk.AIPIdentity.fromApiKey(this.apiKey).then((id) => {
@@ -4280,6 +4294,7 @@ var PrismerClient = class {
4280
4294
  const headers = {};
4281
4295
  if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
4282
4296
  if (this.imAgent) headers["X-IM-Agent"] = this.imAgent;
4297
+ if (this.imWorkspace) headers["X-IM-Workspace"] = this.imWorkspace;
4283
4298
  return headers;
4284
4299
  }
4285
4300
  /**
@@ -4346,6 +4361,9 @@ var PrismerClient = class {
4346
4361
  if (this.imAgent) {
4347
4362
  headers["X-IM-Agent"] = this.imAgent;
4348
4363
  }
4364
+ if (this.imWorkspace) {
4365
+ headers["X-IM-Workspace"] = this.imWorkspace;
4366
+ }
4349
4367
  if (opts?.headers) {
4350
4368
  for (const [k, v] of Object.entries(opts.headers)) headers[k] = v;
4351
4369
  }
@@ -5714,7 +5732,7 @@ function register3(parent, getIMClient2, _getAPIClient) {
5714
5732
  const maxIterations = 30;
5715
5733
  let lastStatus;
5716
5734
  for (let i = 0; i < maxIterations; i++) {
5717
- await new Promise((resolve5) => setTimeout(resolve5, 2e3));
5735
+ await new Promise((resolve6) => setTimeout(resolve6, 2e3));
5718
5736
  if (!opts.json) process.stdout.write(".");
5719
5737
  const statusRes = await client.im.evolution.getReportStatus(traceId);
5720
5738
  if (!statusRes.ok) break;
@@ -6098,11 +6116,94 @@ function register3(parent, getIMClient2, _getAPIClient) {
6098
6116
 
6099
6117
  // src/commands/task.ts
6100
6118
  var import_node_fs = require("fs");
6101
- var import_node_path = require("path");
6119
+ var import_node_path2 = require("path");
6102
6120
  var import_node_crypto = require("crypto");
6121
+
6122
+ // src/commands/deliver-proxy.ts
6123
+ var import_node_path = require("path");
6124
+ function detectDeliverProxy(opts) {
6125
+ const taskId = (opts?.taskId || opts?.runId || "").trim() || process.env.PRISMER_TASK_ID || process.env.PRISMER_RUN_ID || "";
6126
+ if (!taskId) return null;
6127
+ const port = (opts?.daemonPort || "").trim() || (process.env.PRISMER_DAEMON_PORT ?? "3210").trim() || "3210";
6128
+ const ctx = {
6129
+ daemonUrl: `http://127.0.0.1:${port}`,
6130
+ taskId
6131
+ };
6132
+ const agentUsername = process.env.PRISMER_AGENT_USERNAME;
6133
+ if (agentUsername) ctx.agentUsername = agentUsername;
6134
+ const conversationId = (opts?.conversationId || "").trim() || process.env.PRISMER_CONVERSATION_ID;
6135
+ if (conversationId) ctx.conversationId = conversationId;
6136
+ return ctx;
6137
+ }
6138
+ async function proxyDeliver(ctx, filePath, mode, conversationId, messageId) {
6139
+ const abs = (0, import_node_path.resolve)(filePath);
6140
+ const body = {
6141
+ taskId: ctx.taskId,
6142
+ path: abs,
6143
+ mode
6144
+ };
6145
+ const conv = conversationId ?? ctx.conversationId;
6146
+ if (mode === "send") {
6147
+ if (!conv) {
6148
+ return { ok: false, status: 400, error: "conversationId is required for send mode" };
6149
+ }
6150
+ body.conversationId = conv;
6151
+ }
6152
+ if (mode === "message-attach") {
6153
+ if (!conv) {
6154
+ return { ok: false, status: 400, error: "conversationId is required for message-attach mode" };
6155
+ }
6156
+ if (!messageId) {
6157
+ return { ok: false, status: 400, error: "messageId is required for message-attach mode" };
6158
+ }
6159
+ body.conversationId = conv;
6160
+ body.messageId = messageId;
6161
+ }
6162
+ if (ctx.agentUsername) body.agentUsername = ctx.agentUsername;
6163
+ let res;
6164
+ try {
6165
+ res = await fetch(`${ctx.daemonUrl}/local/deliver`, {
6166
+ method: "POST",
6167
+ headers: { "Content-Type": "application/json" },
6168
+ body: JSON.stringify(body)
6169
+ });
6170
+ } catch (err) {
6171
+ return {
6172
+ ok: false,
6173
+ status: 0,
6174
+ error: `daemon unreachable at ${ctx.daemonUrl}/local/deliver: ${err instanceof Error ? err.message : String(err)}`
6175
+ };
6176
+ }
6177
+ let parsed = {};
6178
+ try {
6179
+ parsed = await res.json();
6180
+ } catch {
6181
+ }
6182
+ if (res.ok && parsed.ok) {
6183
+ const out = { ok: true, status: res.status };
6184
+ if (parsed.assetId) out.assetId = parsed.assetId;
6185
+ return out;
6186
+ }
6187
+ return {
6188
+ ok: false,
6189
+ status: res.status,
6190
+ error: parsed.error ?? `daemon returned ${res.status}`
6191
+ };
6192
+ }
6193
+
6194
+ // src/commands/task.ts
6103
6195
  var TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "assigned", "running", "review", "completed", "failed", "cancelled"]);
6104
6196
  var TASK_PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high", "urgent"]);
6105
6197
  var TASK_KINDS = /* @__PURE__ */ new Set(["work_item", "goal"]);
6198
+ function assertNotRunId(id) {
6199
+ if (typeof id === "string" && id.startsWith("run_")) {
6200
+ process.stderr.write(
6201
+ `Error: '${id}' is a run id (run_\u2026); a chat reply doesn't need 'cloud task' ops \u2014 the platform closes the turn from your reply.
6202
+ `
6203
+ );
6204
+ process.exit(1);
6205
+ }
6206
+ }
6106
6207
  function parseTaskStatus(raw) {
6107
6208
  if (!raw) return void 0;
6108
6209
  if (TASK_STATUSES.has(raw)) return raw;
@@ -6325,6 +6426,7 @@ ${tasks.length} task(s) listed.
6325
6426
  }
6326
6427
  });
6327
6428
  task.command("move-project <task-id> [project-id]").description("Move a task to a project, or use --unscoped to return it to workspace-level").option("--unscoped", "set projectId to null", false).option("--json", "output raw JSON response").action(async (taskId, projectId, opts) => {
6429
+ assertNotRunId(taskId);
6328
6430
  const client = getIMClient2();
6329
6431
  try {
6330
6432
  if (!opts.unscoped && !projectId) {
@@ -6351,6 +6453,7 @@ ${tasks.length} task(s) listed.
6351
6453
  }
6352
6454
  });
6353
6455
  task.command("get <task-id>").description("Get task details and logs").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6456
+ assertNotRunId(taskId);
6354
6457
  const client = getIMClient2();
6355
6458
  try {
6356
6459
  const res = await client.im.tasks.get(taskId);
@@ -6415,6 +6518,7 @@ Logs (${logs.length}):
6415
6518
  }
6416
6519
  });
6417
6520
  task.command("claim <task-id>").description("Claim a pending task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6521
+ assertNotRunId(taskId);
6418
6522
  const client = getIMClient2();
6419
6523
  try {
6420
6524
  const res = await client.im.tasks.claim(taskId);
@@ -6445,6 +6549,7 @@ Logs (${logs.length}):
6445
6549
  }
6446
6550
  });
6447
6551
  task.command("update <task-id>").description("Update a task").option("--title <title>", "new title").option("--description <description>", "new description").option("--status <status>", "new status").option("--progress <progress>", "progress (0.0 to 1.0)", parseFloat).option("--status-message <statusMessage>", "status message").option("--skip-checkpoint", "bypass local daemon checkpoint when transitioning to review (release201/09 \xA79.4a.7)", false).option("--json", "output raw JSON response").action(async (taskId, opts) => {
6552
+ assertNotRunId(taskId);
6448
6553
  const client = getIMClient2();
6449
6554
  try {
6450
6555
  if (opts.status === "review" && !opts.skipCheckpoint) {
@@ -6452,7 +6557,7 @@ Logs (${logs.length}):
6452
6557
  if (!checkpoint.ok) {
6453
6558
  if (checkpoint.pendingFiles && checkpoint.pendingFiles.length > 0) {
6454
6559
  process.stderr.write(
6455
- `[checkpoint] result/ \u542B ${checkpoint.pendingFiles.length} \u4E2A\u672A attach \u6587\u4EF6:
6560
+ `[checkpoint] artifacts/ \u542B ${checkpoint.pendingFiles.length} \u4E2A\u672A attach \u6587\u4EF6:
6456
6561
  `
6457
6562
  );
6458
6563
  for (const f of checkpoint.pendingFiles) {
@@ -6513,6 +6618,7 @@ Logs (${logs.length}):
6513
6618
  }
6514
6619
  });
6515
6620
  task.command("complete <task-id>").description("Mark a task as complete").option("--result <result>", "result or output of the task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6621
+ assertNotRunId(taskId);
6516
6622
  const client = getIMClient2();
6517
6623
  try {
6518
6624
  const res = await client.im.tasks.complete(taskId, {
@@ -6547,6 +6653,7 @@ Logs (${logs.length}):
6547
6653
  }
6548
6654
  });
6549
6655
  task.command("fail <task-id>").description("Mark a task as failed").requiredOption("--error <error>", "error message describing why the task failed").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6656
+ assertNotRunId(taskId);
6550
6657
  const client = getIMClient2();
6551
6658
  try {
6552
6659
  const res = await client.im.tasks.fail(taskId, opts.error);
@@ -6579,6 +6686,7 @@ Logs (${logs.length}):
6579
6686
  }
6580
6687
  });
6581
6688
  task.command("approve <task-id>").description("Approve a completed task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6689
+ assertNotRunId(taskId);
6582
6690
  const client = getIMClient2();
6583
6691
  try {
6584
6692
  const res = await client.im.tasks.approve(taskId);
@@ -6609,6 +6717,7 @@ Logs (${logs.length}):
6609
6717
  }
6610
6718
  });
6611
6719
  task.command("reject <task-id>").description("Reject a task").requiredOption("--reason <reason>", "reason for rejection").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6720
+ assertNotRunId(taskId);
6612
6721
  const client = getIMClient2();
6613
6722
  try {
6614
6723
  const res = await client.im.tasks.reject(taskId, opts.reason);
@@ -6638,14 +6747,34 @@ Logs (${logs.length}):
6638
6747
  process.exit(1);
6639
6748
  }
6640
6749
  });
6641
- task.command("attach <path>").description("Attach a file to a task as a user-deliverable (release201/09 \xA79.4a.5)").option("--task <taskId>", "target task id; defaults to PRISMER_TASK_ID env").option("--name <displayName>", "override the display filename; defaults to basename(path)").option("--json", "output raw JSON response").action(async (filePath, opts) => {
6642
- const client = getIMClient2();
6643
- try {
6644
- const targetTaskId = opts.task ?? process.env.PRISMER_TASK_ID;
6645
- if (!targetTaskId) {
6646
- process.stderr.write("Error: --task is required (or set PRISMER_TASK_ID env)\n");
6750
+ task.command("attach <path>").description("Attach a file to a task as a user-deliverable (release201/09 \xA79.4a.5)").option("--task <taskId>", "target task id; defaults to PRISMER_TASK_ID env").option("--name <displayName>", "override the display filename; defaults to basename(path)").option("--run-id <id>", "dispatch task id (alias for --task; from <execution_context>; env fallback PRISMER_TASK_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "output raw JSON response").action(async (filePath, opts) => {
6751
+ const targetTaskId = opts.task ?? opts.runId ?? process.env.PRISMER_TASK_ID;
6752
+ if (!targetTaskId) {
6753
+ process.stderr.write("Error: --task is required (or set PRISMER_TASK_ID env)\n");
6754
+ process.exit(1);
6755
+ }
6756
+ assertNotRunId(targetTaskId);
6757
+ const proxy = detectDeliverProxy({
6758
+ taskId: targetTaskId,
6759
+ daemonPort: opts.daemonPort
6760
+ });
6761
+ if (proxy) {
6762
+ const result = await proxyDeliver(proxy, filePath, "task-attach");
6763
+ if (!result.ok) {
6764
+ process.stderr.write(`Error: ${result.error ?? "task attach failed"}
6765
+ `);
6647
6766
  process.exit(1);
6648
6767
  }
6768
+ if (opts.json) {
6769
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
6770
+ return;
6771
+ }
6772
+ process.stdout.write(`Attached to task ${targetTaskId} (assetId: ${result.assetId ?? "-"})
6773
+ `);
6774
+ return;
6775
+ }
6776
+ const client = getIMClient2();
6777
+ try {
6649
6778
  const getRes = await client.im.tasks.get(targetTaskId);
6650
6779
  if (!getRes.ok || !getRes.data) {
6651
6780
  process.stderr.write(`Error: task ${targetTaskId} not found: ${getRes.error?.message ?? "unknown"}
@@ -6667,7 +6796,7 @@ Logs (${logs.length}):
6667
6796
  process.exit(1);
6668
6797
  }
6669
6798
  const contentHash = (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
6670
- const displayName = opts.name ?? (0, import_node_path.basename)(filePath);
6799
+ const displayName = opts.name ?? (0, import_node_path2.basename)(filePath);
6671
6800
  const uploadRes = await client.im.assets.upload(bytes, {
6672
6801
  workspaceId: wsId,
6673
6802
  sourceTaskId: targetTaskId,
@@ -6716,6 +6845,7 @@ Logs (${logs.length}):
6716
6845
  }
6717
6846
  });
6718
6847
  task.command("cancel <task-id>").description("Cancel a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6848
+ assertNotRunId(taskId);
6719
6849
  const client = getIMClient2();
6720
6850
  try {
6721
6851
  const res = await client.im.tasks.cancel(taskId);
@@ -6746,6 +6876,7 @@ Logs (${logs.length}):
6746
6876
  }
6747
6877
  });
6748
6878
  task.command("spec-set <task-id>").description("Owner writes / updates SPEC.md for a task").option("-f, --file <path>", "read SPEC.md content from a file").option("-m, --markdown <markdown>", "inline SPEC.md content").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6879
+ assertNotRunId(taskId);
6749
6880
  const client = getIMClient2();
6750
6881
  try {
6751
6882
  let md = "";
@@ -6776,6 +6907,7 @@ Logs (${logs.length}):
6776
6907
  }
6777
6908
  });
6778
6909
  task.command("spec-show <task-id>").description("Print SPEC.md content for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
6910
+ assertNotRunId(taskId);
6779
6911
  const client = getIMClient2();
6780
6912
  try {
6781
6913
  const res = await client.im.tasks.spec.get(taskId);
@@ -6800,6 +6932,7 @@ Logs (${logs.length}):
6800
6932
  }
6801
6933
  });
6802
6934
  task.command("todo-add <task-id> <text...>").description("Append an item to the task TODO.md (assignee)").option("--depth <n>", "nesting depth (0..3)", (v) => parseInt(v, 10), 0).option("--json", "output raw JSON response").action(async (taskId, text, opts) => {
6935
+ assertNotRunId(taskId);
6803
6936
  const client = getIMClient2();
6804
6937
  try {
6805
6938
  const res = await client.im.tasks.todo.add(taskId, {
@@ -6825,6 +6958,7 @@ Logs (${logs.length}):
6825
6958
  }
6826
6959
  });
6827
6960
  task.command("todo-done <task-id> <index>").description("Mark a TODO item as done").option("--json", "output raw JSON response").action(async (taskId, indexRaw, opts) => {
6961
+ assertNotRunId(taskId);
6828
6962
  const client = getIMClient2();
6829
6963
  try {
6830
6964
  const idx = parseInt(indexRaw, 10);
@@ -6851,6 +6985,7 @@ Logs (${logs.length}):
6851
6985
  }
6852
6986
  });
6853
6987
  task.command("todo-uncheck <task-id> <index>").description("Un-tick a TODO item").option("--json", "output raw JSON response").action(async (taskId, indexRaw, opts) => {
6988
+ assertNotRunId(taskId);
6854
6989
  const client = getIMClient2();
6855
6990
  try {
6856
6991
  const idx = parseInt(indexRaw, 10);
@@ -6875,6 +7010,7 @@ Logs (${logs.length}):
6875
7010
  }
6876
7011
  });
6877
7012
  task.command("todo-show <task-id>").description("Render TODO.md checklist + progress for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
7013
+ assertNotRunId(taskId);
6878
7014
  const client = getIMClient2();
6879
7015
  try {
6880
7016
  const res = await client.im.tasks.todo.list(taskId);
@@ -6905,6 +7041,7 @@ Logs (${logs.length}):
6905
7041
  }
6906
7042
  });
6907
7043
  task.command("acceptance <task-id>").description("Show acceptance criteria + rolled-up status for a task").option("--json", "output raw JSON response").action(async (taskId, opts) => {
7044
+ assertNotRunId(taskId);
6908
7045
  const client = getIMClient2();
6909
7046
  try {
6910
7047
  const res = await client.im.tasks.getAcceptance(taskId);
@@ -6937,6 +7074,7 @@ Logs (${logs.length}):
6937
7074
  "--mode <mode>",
6938
7075
  "verifyMode: qualitative | quantitative | agent-self-check | manual"
6939
7076
  ).requiredOption("--expectation <markdown>", "markdown describing what done looks like").option("--verifier-agent <agentId>", "verifier agent id (default = creator)").option("--weight <n>", "weight (numeric, default 1)", parseFloat).option("--optional", "mark this criterion optional (default required)").option("--json", "output raw JSON response").action(async (taskId, opts) => {
7077
+ assertNotRunId(taskId);
6940
7078
  const client = getIMClient2();
6941
7079
  try {
6942
7080
  const VALID = ["qualitative", "quantitative", "agent-self-check", "manual"];
@@ -6971,6 +7109,7 @@ Logs (${logs.length}):
6971
7109
  task.command("verify <task-id>").description(
6972
7110
  "Assignee self-check: list all agent-self-check criteria + mark them passed (run before status=review)"
6973
7111
  ).option("--note <note>", "note to attach to each self-check", "agent self-check via `cloud task verify`").option("--json", "output raw JSON response").action(async (taskId, opts) => {
7112
+ assertNotRunId(taskId);
6974
7113
  const client = getIMClient2();
6975
7114
  try {
6976
7115
  const view = await client.im.tasks.getAcceptance(taskId);
@@ -7015,6 +7154,7 @@ Logs (${logs.length}):
7015
7154
  task.command("verify-criterion <task-id> <criterion-id>").description(
7016
7155
  "Report verify outcome for one criterion. Used by reviewers (manual), verifier agents, and assignees (self-check)."
7017
7156
  ).requiredOption("--outcome <outcome>", "passed | failed | n/a | waived").option("--note <note>", "free-form markdown: method + result + repro steps").option("--evidence <ref...>", "evidence refs (repeatable; e.g. asset:<id>, url:..., taskRun:<id>)").option("--waive-reason <reason>", "required when --outcome waived").option("--json", "output raw JSON response").action(async (taskId, criterionId, opts) => {
7157
+ assertNotRunId(taskId);
7018
7158
  const client = getIMClient2();
7019
7159
  try {
7020
7160
  const VALID = ["passed", "failed", "n/a", "waived"];
@@ -7049,6 +7189,7 @@ Logs (${logs.length}):
7049
7189
  }
7050
7190
  });
7051
7191
  task.command("apply-template <task-id>").description("Apply an acceptance criteria template to a task").requiredOption("--template <templateId>", "template id to apply").option("--json", "output raw JSON response").action(async (taskId, opts) => {
7192
+ assertNotRunId(taskId);
7052
7193
  const client = getIMClient2();
7053
7194
  try {
7054
7195
  const res = await client.im.tasks.applyTemplate(taskId, opts.template);
@@ -8092,7 +8233,9 @@ function register9(parent, _getIMClient, _getAPIClient) {
8092
8233
  // src/commands/files.ts
8093
8234
  function register10(parent, getIMClient2, _getAPIClient) {
8094
8235
  const file = parent.command("file").description("File upload, transfer, quota, and type management");
8095
- file.command("upload <path>").description("Upload a file and get its upload ID and CDN URL").option("--mime <type>", "Override MIME type (e.g. image/png)").option("--json", "Output raw JSON response").action(async (filePath, opts) => {
8236
+ file.command("upload <path>").description(
8237
+ "Upload bytes only \u2192 returns an upload ID + CDN URL. NOT a delivery: nothing reaches the user. To deliver an artifact use `cloud deliver <path>`."
8238
+ ).option("--mime <type>", "Override MIME type (e.g. image/png)").option("--json", "Output raw JSON response").action(async (filePath, opts) => {
8096
8239
  const client = getIMClient2();
8097
8240
  try {
8098
8241
  const uploadOpts = {};
@@ -8112,13 +8255,39 @@ function register10(parent, getIMClient2, _getAPIClient) {
8112
8255
  `);
8113
8256
  process.stdout.write(`MIME: ${res.mimeType}
8114
8257
  `);
8258
+ process.stdout.write(
8259
+ "Note: this only uploaded bytes \u2014 nothing was delivered to the user. To deliver an artifact, run `cloud deliver <abs-path>`.\n"
8260
+ );
8115
8261
  } catch (err) {
8116
8262
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
8117
8263
  `);
8118
8264
  process.exit(1);
8119
8265
  }
8120
8266
  });
8121
- file.command("send <conversation-id> <path>").description("Upload a file and send it as a message in a conversation").option("-c, --content <text>", "Optional text caption to accompany the file").option("--mime <type>", "Override MIME type").option("--json", "Output raw JSON response").action(async (conversationId, filePath, opts) => {
8267
+ file.command("send <conversation-id> <path>").description(
8268
+ "Send a file as its OWN standalone message (ad-hoc sharing). For a final deliverable use `cloud deliver` instead \u2014 do not run both on the same file (double-delivery)."
8269
+ ).option("-c, --content <text>", "Optional text caption to accompany the file").option("--mime <type>", "Override MIME type").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id (alternative source; positional <conversation-id> takes precedence)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "Output raw JSON response").action(async (conversationIdArg, filePath, opts) => {
8270
+ const conversationId = conversationIdArg || opts.conversationId || "";
8271
+ const proxy = detectDeliverProxy({
8272
+ runId: opts.runId,
8273
+ conversationId,
8274
+ daemonPort: opts.daemonPort
8275
+ });
8276
+ if (proxy) {
8277
+ const result = await proxyDeliver(proxy, filePath, "send", conversationId);
8278
+ if (!result.ok) {
8279
+ process.stderr.write(`Error: ${result.error ?? "delivery failed"}
8280
+ `);
8281
+ process.exit(1);
8282
+ }
8283
+ if (opts.json) {
8284
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
8285
+ return;
8286
+ }
8287
+ process.stdout.write(`File sent to conversation ${conversationId} (assetId: ${result.assetId ?? "-"})
8288
+ `);
8289
+ return;
8290
+ }
8122
8291
  const client = getIMClient2();
8123
8292
  try {
8124
8293
  const sendOpts = {};
@@ -9482,13 +9651,13 @@ function parseIntOpt2(value) {
9482
9651
  var import_node_crypto2 = require("crypto");
9483
9652
  var import_node_fs3 = require("fs");
9484
9653
  var import_node_os = require("os");
9485
- var import_node_path2 = require("path");
9654
+ var import_node_path3 = require("path");
9486
9655
  var import_node_child_process = require("child_process");
9487
9656
  function resolvePrismerRoot() {
9488
- return process.env.PRISMER_HOME ? (0, import_node_path2.resolve)(process.env.PRISMER_HOME) : (0, import_node_path2.join)((0, import_node_os.homedir)(), ".prismer");
9657
+ return process.env.PRISMER_HOME ? (0, import_node_path3.resolve)(process.env.PRISMER_HOME) : (0, import_node_path3.join)((0, import_node_os.homedir)(), ".prismer");
9489
9658
  }
9490
9659
  function readDaemonConfig() {
9491
- const path6 = (0, import_node_path2.join)(resolvePrismerRoot(), "config.toml");
9660
+ const path6 = (0, import_node_path3.join)(resolvePrismerRoot(), "config.toml");
9492
9661
  if (!(0, import_node_fs3.existsSync)(path6)) {
9493
9662
  throw new Error(`${path6} not found. Run \`prismer setup\` first.`);
9494
9663
  }
@@ -9501,7 +9670,7 @@ function readDaemonConfig() {
9501
9670
  return { daemonId: idMatch[1], apiKey: keyMatch[1] };
9502
9671
  }
9503
9672
  function resolveAgentDir(daemonId, agentId) {
9504
- return (0, import_node_path2.join)(resolvePrismerRoot(), "devices", daemonId, "agents", agentId);
9673
+ return (0, import_node_path3.join)(resolvePrismerRoot(), "devices", daemonId, "agents", agentId);
9505
9674
  }
9506
9675
  function collectFiles(root, excludeBasenames) {
9507
9676
  const out = [];
@@ -9514,7 +9683,7 @@ function collectFiles(root, excludeBasenames) {
9514
9683
  }
9515
9684
  for (const name of entries) {
9516
9685
  if (excludeBasenames.has(name)) continue;
9517
- const full = (0, import_node_path2.join)(dir, name);
9686
+ const full = (0, import_node_path3.join)(dir, name);
9518
9687
  let st;
9519
9688
  try {
9520
9689
  st = (0, import_node_fs3.statSync)(full);
@@ -9526,7 +9695,7 @@ function collectFiles(root, excludeBasenames) {
9526
9695
  const buf = (0, import_node_fs3.readFileSync)(full);
9527
9696
  const sha = (0, import_node_crypto2.createHash)("sha256").update(buf).digest("hex");
9528
9697
  out.push({
9529
- rel: (0, import_node_path2.relative)(root, full).split(import_node_path2.sep).join("/"),
9698
+ rel: (0, import_node_path3.relative)(root, full).split(import_node_path3.sep).join("/"),
9530
9699
  sha,
9531
9700
  size: buf.byteLength
9532
9701
  });
@@ -9652,11 +9821,11 @@ function register17(parent, getIMClient2, _getAPIClient) {
9652
9821
  sha256,
9653
9822
  signature
9654
9823
  };
9655
- (0, import_node_fs3.writeFileSync)((0, import_node_path2.join)(agentDir, "transfer-manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
9656
- const parentOfAgents = (0, import_node_path2.join)(resolvePrismerRoot(), "devices", daemonId);
9657
- const outputAbs = (0, import_node_path2.resolve)(opts.output);
9824
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(agentDir, "transfer-manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
9825
+ const parentOfAgents = (0, import_node_path3.join)(resolvePrismerRoot(), "devices", daemonId);
9826
+ const outputAbs = (0, import_node_path3.resolve)(opts.output);
9658
9827
  try {
9659
- (0, import_node_child_process.execFileSync)("tar", ["-czf", outputAbs, "-C", parentOfAgents, (0, import_node_path2.join)("agents", agentId)], { stdio: "inherit" });
9828
+ (0, import_node_child_process.execFileSync)("tar", ["-czf", outputAbs, "-C", parentOfAgents, (0, import_node_path3.join)("agents", agentId)], { stdio: "inherit" });
9660
9829
  } catch (err) {
9661
9830
  process.stderr.write(`Error: tar failed: ${err.message}
9662
9831
  `);
@@ -9686,14 +9855,14 @@ function register17(parent, getIMClient2, _getAPIClient) {
9686
9855
  }
9687
9856
  });
9688
9857
  agent.command("import <tar-file>").description("release201/09 \xA79.7 \u2014 extract + rebind an agent transferred from another device").option("--json", "Output raw JSON result").action(async (tarFile, opts) => {
9689
- const tarAbs = (0, import_node_path2.resolve)(tarFile);
9858
+ const tarAbs = (0, import_node_path3.resolve)(tarFile);
9690
9859
  if (!(0, import_node_fs3.existsSync)(tarAbs)) {
9691
9860
  process.stderr.write(`Error: tar file not found: ${tarAbs}
9692
9861
  `);
9693
9862
  process.exit(1);
9694
9863
  }
9695
9864
  const { daemonId: toDaemonId, apiKey } = readDaemonConfig();
9696
- const tmpRoot = (0, import_node_path2.join)(resolvePrismerRoot(), ".transfer-staging", `${Date.now()}`);
9865
+ const tmpRoot = (0, import_node_path3.join)(resolvePrismerRoot(), ".transfer-staging", `${Date.now()}`);
9697
9866
  (0, import_node_fs3.mkdirSync)(tmpRoot, { recursive: true });
9698
9867
  try {
9699
9868
  (0, import_node_child_process.execFileSync)("tar", ["-xzf", tarAbs, "-C", tmpRoot], { stdio: "inherit" });
@@ -9703,7 +9872,7 @@ function register17(parent, getIMClient2, _getAPIClient) {
9703
9872
  `);
9704
9873
  process.exit(1);
9705
9874
  }
9706
- const agentsRoot = (0, import_node_path2.join)(tmpRoot, "agents");
9875
+ const agentsRoot = (0, import_node_path3.join)(tmpRoot, "agents");
9707
9876
  if (!(0, import_node_fs3.existsSync)(agentsRoot)) {
9708
9877
  (0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
9709
9878
  process.stderr.write("Error: tar layout invalid \u2014 no agents/ root inside archive\n");
@@ -9717,8 +9886,8 @@ function register17(parent, getIMClient2, _getAPIClient) {
9717
9886
  process.exit(1);
9718
9887
  }
9719
9888
  const agentId = entries[0];
9720
- const stagedAgentDir = (0, import_node_path2.join)(agentsRoot, agentId);
9721
- const manifestPath = (0, import_node_path2.join)(stagedAgentDir, "transfer-manifest.json");
9889
+ const stagedAgentDir = (0, import_node_path3.join)(agentsRoot, agentId);
9890
+ const manifestPath = (0, import_node_path3.join)(stagedAgentDir, "transfer-manifest.json");
9722
9891
  if (!(0, import_node_fs3.existsSync)(manifestPath)) {
9723
9892
  (0, import_node_fs3.rmSync)(tmpRoot, { recursive: true, force: true });
9724
9893
  process.stderr.write("Error: transfer-manifest.json missing from archive\n");
@@ -9766,7 +9935,7 @@ function register17(parent, getIMClient2, _getAPIClient) {
9766
9935
  );
9767
9936
  process.exit(1);
9768
9937
  }
9769
- (0, import_node_fs3.mkdirSync)((0, import_node_path2.join)(resolvePrismerRoot(), "devices", toDaemonId, "agents"), { recursive: true });
9938
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)(resolvePrismerRoot(), "devices", toDaemonId, "agents"), { recursive: true });
9770
9939
  try {
9771
9940
  (0, import_node_child_process.execFileSync)("mv", [stagedAgentDir, targetAgentDir]);
9772
9941
  } catch {
@@ -9817,7 +9986,7 @@ function register17(parent, getIMClient2, _getAPIClient) {
9817
9986
  });
9818
9987
  agent.command("cleanup-orphan").description("release201/09 \xA79.7.3 \u2014 list devices/*/agents/* dirs whose cloud binding points elsewhere").option("--delete", "Actually remove orphan dirs (default: dry-run)").option("--json", "Output raw JSON list").action(async (opts) => {
9819
9988
  const { daemonId } = readDaemonConfig();
9820
- const devicesRoot = (0, import_node_path2.join)(resolvePrismerRoot(), "devices");
9989
+ const devicesRoot = (0, import_node_path3.join)(resolvePrismerRoot(), "devices");
9821
9990
  if (!(0, import_node_fs3.existsSync)(devicesRoot)) {
9822
9991
  if (opts.json) process.stdout.write("[]\n");
9823
9992
  else console.log("No devices/ dir yet.");
@@ -9826,10 +9995,10 @@ function register17(parent, getIMClient2, _getAPIClient) {
9826
9995
  const orphans = [];
9827
9996
  for (const did of (0, import_node_fs3.readdirSync)(devicesRoot)) {
9828
9997
  if (did === daemonId) continue;
9829
- const agentsRoot = (0, import_node_path2.join)(devicesRoot, did, "agents");
9998
+ const agentsRoot = (0, import_node_path3.join)(devicesRoot, did, "agents");
9830
9999
  if (!(0, import_node_fs3.existsSync)(agentsRoot)) continue;
9831
10000
  for (const aid of (0, import_node_fs3.readdirSync)(agentsRoot)) {
9832
- const dir = (0, import_node_path2.join)(agentsRoot, aid);
10001
+ const dir = (0, import_node_path3.join)(agentsRoot, aid);
9833
10002
  try {
9834
10003
  const st = (0, import_node_fs3.statSync)(dir);
9835
10004
  if (!st.isDirectory()) continue;
@@ -10578,16 +10747,32 @@ function getIMClient() {
10578
10747
  const cfg = readConfig();
10579
10748
  const env = cfg?.default?.environment || "production";
10580
10749
  const baseUrl = cfg?.default?.base_url || "";
10750
+ const agentUsername = process.env.PRISMER_AGENT_USERNAME;
10751
+ const imAgentOpt = agentUsername ? { imAgent: agentUsername } : {};
10752
+ const workspaceId = process.env.PRISMER_WORKSPACE_ID;
10753
+ const imWorkspaceOpt = workspaceId ? { imWorkspace: workspaceId } : {};
10581
10754
  const imToken = cfg?.auth?.im_token;
10582
10755
  if (imToken) {
10583
- return new PrismerClient({ apiKey: imToken, environment: env, ...baseUrl ? { baseUrl } : {} });
10756
+ return new PrismerClient({
10757
+ apiKey: imToken,
10758
+ environment: env,
10759
+ ...baseUrl ? { baseUrl } : {},
10760
+ ...imAgentOpt,
10761
+ ...imWorkspaceOpt
10762
+ });
10584
10763
  }
10585
10764
  const apiKey = cfg?.default?.api_key;
10586
10765
  if (!apiKey) {
10587
10766
  errorLine('No credentials. Run "cloud setup" first (or "cloud setup --agent" / "cloud register <username>" for IM-JWT path).');
10588
10767
  process.exit(1);
10589
10768
  }
10590
- return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {} });
10769
+ return new PrismerClient({
10770
+ apiKey,
10771
+ environment: env,
10772
+ ...baseUrl ? { baseUrl } : {},
10773
+ ...imAgentOpt,
10774
+ ...imWorkspaceOpt
10775
+ });
10591
10776
  }
10592
10777
  function getAPIClient() {
10593
10778
  const cfg = readConfig();
@@ -10598,7 +10783,11 @@ function getAPIClient() {
10598
10783
  }
10599
10784
  const env = cfg?.default?.environment || "production";
10600
10785
  const baseUrl = cfg?.default?.base_url || "";
10601
- return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {} });
10786
+ const agentUsername = process.env.PRISMER_AGENT_USERNAME;
10787
+ const imAgentOpt = agentUsername ? { imAgent: agentUsername } : {};
10788
+ const workspaceId = process.env.PRISMER_WORKSPACE_ID;
10789
+ const imWorkspaceOpt = workspaceId ? { imWorkspace: workspaceId } : {};
10790
+ return new PrismerClient({ apiKey, environment: env, ...baseUrl ? { baseUrl } : {}, ...imAgentOpt, ...imWorkspaceOpt });
10602
10791
  }
10603
10792
  var program = new import_commander.Command();
10604
10793
  program.name("cloud").description("Prismer Cloud SDK CLI").version(cliVersion);
@@ -10997,6 +11186,59 @@ program.command("send").description("Send a direct message (shortcut for: im sen
10997
11186
  }
10998
11187
  success(`Message sent (conversation: ${res.data?.conversationId})`);
10999
11188
  });
11189
+ program.command("deliver <path>").description("Attach a file you wrote to your current reply (in-container explicit delivery)").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id (from <execution_context>; env fallback PRISMER_CONVERSATION_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "JSON output").action(async (filePath, opts) => {
11190
+ const proxy = detectDeliverProxy({
11191
+ runId: opts.runId,
11192
+ conversationId: opts.conversationId,
11193
+ daemonPort: opts.daemonPort
11194
+ });
11195
+ if (!proxy) {
11196
+ errorLine(
11197
+ "cloud deliver only works inside a daemon dispatch (no PRISMER_TASK_ID/PRISMER_RUN_ID, and no --run-id flag). On hermes, pass --run-id <id> (and --conversation-id <id>) copied from <execution_context>. Outside a dispatch, use `cloud file send <conversationId> <path>`."
11198
+ );
11199
+ process.exit(1);
11200
+ }
11201
+ const result = await proxyDeliver(proxy, filePath, "attach");
11202
+ if (!result.ok) {
11203
+ errorLine(`Delivery failed: ${result.error ?? `daemon returned ${result.status}`}`);
11204
+ process.exit(1);
11205
+ }
11206
+ if (opts.json) {
11207
+ console.log(JSON.stringify(result, null, 2));
11208
+ return;
11209
+ }
11210
+ success(`Attached to your reply (assetId: ${result.assetId ?? "-"})`);
11211
+ });
11212
+ program.command("attach <messageId> <path>").description("Attach a file to a message you ALREADY sent (by its messageId)").option("--run-id <id>", "dispatch run/task id (from <execution_context>; env fallback PRISMER_TASK_ID/RUN_ID)").option("--conversation-id <id>", "conversation id of the target message (from <execution_context>; env fallback PRISMER_CONVERSATION_ID)").option("--daemon-port <port>", "daemon local-server port (env fallback PRISMER_DAEMON_PORT, default 3210)").option("--json", "JSON output").action(async (messageId, filePath, opts) => {
11213
+ const proxy = detectDeliverProxy({
11214
+ runId: opts.runId,
11215
+ conversationId: opts.conversationId,
11216
+ daemonPort: opts.daemonPort
11217
+ });
11218
+ if (!proxy) {
11219
+ errorLine(
11220
+ "cloud attach only works inside a daemon dispatch (no PRISMER_TASK_ID/PRISMER_RUN_ID, and no --run-id flag). On hermes, pass --run-id <id> and --conversation-id <id> copied from <execution_context>."
11221
+ );
11222
+ process.exit(1);
11223
+ }
11224
+ const conversationId = (opts.conversationId || proxy.conversationId || "").trim();
11225
+ if (!conversationId) {
11226
+ errorLine(
11227
+ "cloud attach needs the conversation id of the target message. Set PRISMER_CONVERSATION_ID or pass --conversation-id <id> (copy it from <execution_context>)."
11228
+ );
11229
+ process.exit(1);
11230
+ }
11231
+ const result = await proxyDeliver(proxy, filePath, "message-attach", conversationId, messageId);
11232
+ if (!result.ok) {
11233
+ errorLine(`Attach failed: ${result.error ?? `daemon returned ${result.status}`}`);
11234
+ process.exit(1);
11235
+ }
11236
+ if (opts.json) {
11237
+ console.log(JSON.stringify(result, null, 2));
11238
+ return;
11239
+ }
11240
+ success(`Attached to message ${messageId} (assetId: ${result.assetId ?? "-"})`);
11241
+ });
11000
11242
  program.command("emit <namespace.name>").description("Emit a metric event (shortcut for: metric emit)").option("--value <value>", "metric value (number or string)").option("--dim <k=v>", "dimension (repeatable; workspaceId is required)", (val, prev = []) => {
11001
11243
  prev.push(val);
11002
11244
  return prev;
package/dist/index.d.mts CHANGED
@@ -258,6 +258,13 @@ interface PrismerConfig {
258
258
  fetch?: typeof fetch;
259
259
  /** Default X-IM-Agent header for IM requests (select which agent identity to use) */
260
260
  imAgent?: string;
261
+ /**
262
+ * Default X-IM-Workspace header for IM requests (release202/09 §3.6 B).
263
+ * Lets the cloud agent-proxy reach its workspace-scoped fallback when the
264
+ * owner `userId`/`numericId` bridge can't resolve the agent. Optional —
265
+ * absent header preserves legacy behavior.
266
+ */
267
+ imWorkspace?: string;
261
268
  /** Enable offline-first mode for IM with local persistence and sync */
262
269
  offline?: OfflineConfig;
263
270
  /**
@@ -5284,6 +5291,7 @@ declare class PrismerClient {
5284
5291
  private readonly timeout;
5285
5292
  private readonly fetchFn;
5286
5293
  private readonly imAgent?;
5294
+ private readonly imWorkspace?;
5287
5295
  private _offlineManager;
5288
5296
  /** AIP identity for auto-signing (v1.8.0 S1) */
5289
5297
  private _identity;
package/dist/index.d.ts CHANGED
@@ -258,6 +258,13 @@ interface PrismerConfig {
258
258
  fetch?: typeof fetch;
259
259
  /** Default X-IM-Agent header for IM requests (select which agent identity to use) */
260
260
  imAgent?: string;
261
+ /**
262
+ * Default X-IM-Workspace header for IM requests (release202/09 §3.6 B).
263
+ * Lets the cloud agent-proxy reach its workspace-scoped fallback when the
264
+ * owner `userId`/`numericId` bridge can't resolve the agent. Optional —
265
+ * absent header preserves legacy behavior.
266
+ */
267
+ imWorkspace?: string;
261
268
  /** Enable offline-first mode for IM with local persistence and sync */
262
269
  offline?: OfflineConfig;
263
270
  /**
@@ -5284,6 +5291,7 @@ declare class PrismerClient {
5284
5291
  private readonly timeout;
5285
5292
  private readonly fetchFn;
5286
5293
  private readonly imAgent?;
5294
+ private readonly imWorkspace?;
5287
5295
  private _offlineManager;
5288
5296
  /** AIP identity for auto-signing (v1.8.0 S1) */
5289
5297
  private _identity;
package/dist/index.js CHANGED
@@ -5579,6 +5579,19 @@ var FilesClient = class {
5579
5579
  */
5580
5580
  async sendFile(conversationId, input, opts) {
5581
5581
  const uploaded = await this.upload(input, opts);
5582
+ const outboxDir = process.env.PRISMER_OUTBOX_DIR;
5583
+ if (outboxDir) {
5584
+ try {
5585
+ const fsMod = await import("fs");
5586
+ const pathMod = await import("path");
5587
+ const srcPath = typeof input === "string" ? input : input.path;
5588
+ if (srcPath && typeof srcPath === "string") {
5589
+ await fsMod.promises.mkdir(outboxDir, { recursive: true });
5590
+ fsMod.cpSync(srcPath, pathMod.join(outboxDir, pathMod.basename(srcPath)));
5591
+ }
5592
+ } catch {
5593
+ }
5594
+ }
5582
5595
  const msgRes = await this._r("POST", `/api/im/messages/${conversationId}`, {
5583
5596
  content: opts?.content || uploaded.fileName,
5584
5597
  type: "file",
@@ -5895,6 +5908,7 @@ var PrismerClient = class {
5895
5908
  this.timeout = config.timeout || 3e4;
5896
5909
  this.fetchFn = config.fetch || fetch;
5897
5910
  this.imAgent = config.imAgent;
5911
+ this.imWorkspace = config.imWorkspace;
5898
5912
  if (config.identity) {
5899
5913
  if (config.identity === "auto" && this.apiKey) {
5900
5914
  this._identityReady = import_aip_sdk.AIPIdentity.fromApiKey(this.apiKey).then((id) => {
@@ -5991,6 +6005,7 @@ var PrismerClient = class {
5991
6005
  const headers = {};
5992
6006
  if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
5993
6007
  if (this.imAgent) headers["X-IM-Agent"] = this.imAgent;
6008
+ if (this.imWorkspace) headers["X-IM-Workspace"] = this.imWorkspace;
5994
6009
  return headers;
5995
6010
  }
5996
6011
  /**
@@ -6057,6 +6072,9 @@ var PrismerClient = class {
6057
6072
  if (this.imAgent) {
6058
6073
  headers["X-IM-Agent"] = this.imAgent;
6059
6074
  }
6075
+ if (this.imWorkspace) {
6076
+ headers["X-IM-Workspace"] = this.imWorkspace;
6077
+ }
6060
6078
  if (opts?.headers) {
6061
6079
  for (const [k, v] of Object.entries(opts.headers)) headers[k] = v;
6062
6080
  }
package/dist/index.mjs CHANGED
@@ -5497,6 +5497,19 @@ var FilesClient = class {
5497
5497
  */
5498
5498
  async sendFile(conversationId, input, opts) {
5499
5499
  const uploaded = await this.upload(input, opts);
5500
+ const outboxDir = process.env.PRISMER_OUTBOX_DIR;
5501
+ if (outboxDir) {
5502
+ try {
5503
+ const fsMod = await import("fs");
5504
+ const pathMod = await import("path");
5505
+ const srcPath = typeof input === "string" ? input : input.path;
5506
+ if (srcPath && typeof srcPath === "string") {
5507
+ await fsMod.promises.mkdir(outboxDir, { recursive: true });
5508
+ fsMod.cpSync(srcPath, pathMod.join(outboxDir, pathMod.basename(srcPath)));
5509
+ }
5510
+ } catch {
5511
+ }
5512
+ }
5500
5513
  const msgRes = await this._r("POST", `/api/im/messages/${conversationId}`, {
5501
5514
  content: opts?.content || uploaded.fileName,
5502
5515
  type: "file",
@@ -5813,6 +5826,7 @@ var PrismerClient = class {
5813
5826
  this.timeout = config.timeout || 3e4;
5814
5827
  this.fetchFn = config.fetch || fetch;
5815
5828
  this.imAgent = config.imAgent;
5829
+ this.imWorkspace = config.imWorkspace;
5816
5830
  if (config.identity) {
5817
5831
  if (config.identity === "auto" && this.apiKey) {
5818
5832
  this._identityReady = AIPIdentity.fromApiKey(this.apiKey).then((id) => {
@@ -5909,6 +5923,7 @@ var PrismerClient = class {
5909
5923
  const headers = {};
5910
5924
  if (this.apiKey) headers["Authorization"] = `Bearer ${this.apiKey}`;
5911
5925
  if (this.imAgent) headers["X-IM-Agent"] = this.imAgent;
5926
+ if (this.imWorkspace) headers["X-IM-Workspace"] = this.imWorkspace;
5912
5927
  return headers;
5913
5928
  }
5914
5929
  /**
@@ -5975,6 +5990,9 @@ var PrismerClient = class {
5975
5990
  if (this.imAgent) {
5976
5991
  headers["X-IM-Agent"] = this.imAgent;
5977
5992
  }
5993
+ if (this.imWorkspace) {
5994
+ headers["X-IM-Workspace"] = this.imWorkspace;
5995
+ }
5978
5996
  if (opts?.headers) {
5979
5997
  for (const [k, v] of Object.entries(opts.headers)) headers[k] = v;
5980
5998
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismer/sdk",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "Official TypeScript SDK for Prismer Cloud API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",