commonswarm 0.1.18 → 0.1.20
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/cswarm.cjs +643 -42
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -13513,7 +13513,7 @@ __export(cli_exports, {
|
|
|
13513
13513
|
resolveDetachedCodexExecutable: () => resolveDetachedCodexExecutable
|
|
13514
13514
|
});
|
|
13515
13515
|
module.exports = __toCommonJS(cli_exports);
|
|
13516
|
-
var
|
|
13516
|
+
var import_node_crypto19 = require("node:crypto");
|
|
13517
13517
|
var import_node_fs7 = require("node:fs");
|
|
13518
13518
|
var import_promises10 = require("node:fs/promises");
|
|
13519
13519
|
var import_node_path17 = require("node:path");
|
|
@@ -22105,7 +22105,7 @@ registerUpcaster("TaskCreated", 0, (p) => ({ task_id: p.id, slug: p.name }));
|
|
|
22105
22105
|
// src/protocol/workspace-commands.ts
|
|
22106
22106
|
var INVITATION_MAX_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
22107
22107
|
var AGENT_TOKEN_DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
22108
|
-
var AGENT_TOKEN_MAX_TTL_MS =
|
|
22108
|
+
var AGENT_TOKEN_MAX_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
22109
22109
|
var RENEWAL_HORIZON_DEFAULT_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
22110
22110
|
var RENEWAL_HORIZON_MAX_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
22111
22111
|
|
|
@@ -22380,6 +22380,41 @@ async function raceSignalDeadline(work, deadline, callerAbort) {
|
|
|
22380
22380
|
}
|
|
22381
22381
|
return await Promise.race(arms);
|
|
22382
22382
|
}
|
|
22383
|
+
async function declareAgentModel(target2, request, fetcher = fetch) {
|
|
22384
|
+
const controller = new AbortController();
|
|
22385
|
+
const timer2 = setTimeout(() => controller.abort(), 3e4);
|
|
22386
|
+
const onCallerAbort = () => controller.abort();
|
|
22387
|
+
request.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
22388
|
+
if (request.signal?.aborted) controller.abort();
|
|
22389
|
+
let response;
|
|
22390
|
+
try {
|
|
22391
|
+
response = await fetcher(commandEndpoint(target2), {
|
|
22392
|
+
method: "POST",
|
|
22393
|
+
headers: {
|
|
22394
|
+
authorization: `Bearer ${request.credential}`,
|
|
22395
|
+
apikey: target2.anonKey,
|
|
22396
|
+
"content-type": "application/json"
|
|
22397
|
+
},
|
|
22398
|
+
body: JSON.stringify({
|
|
22399
|
+
command_id: request.commandId ?? newCommandId(),
|
|
22400
|
+
client_version: CLIENT_PROTOCOL_VERSION,
|
|
22401
|
+
workspace_id: request.workspaceId,
|
|
22402
|
+
stream: { kind: "workspace" },
|
|
22403
|
+
command: { kind: "declare_agent_model", model: request.model }
|
|
22404
|
+
}),
|
|
22405
|
+
signal: controller.signal
|
|
22406
|
+
});
|
|
22407
|
+
} catch (error) {
|
|
22408
|
+
if (error.name === "AbortError") {
|
|
22409
|
+
throw new CommandTransportError("model declaration timed out");
|
|
22410
|
+
}
|
|
22411
|
+
throw new CommandTransportError("model declaration failed before a response");
|
|
22412
|
+
} finally {
|
|
22413
|
+
clearTimeout(timer2);
|
|
22414
|
+
request.signal?.removeEventListener("abort", onCallerAbort);
|
|
22415
|
+
}
|
|
22416
|
+
return { httpStatus: response.status };
|
|
22417
|
+
}
|
|
22383
22418
|
var ThinCommandClient = class {
|
|
22384
22419
|
constructor(target2, fetcher = fetch) {
|
|
22385
22420
|
this.target = target2;
|
|
@@ -22751,8 +22786,280 @@ var ThinCommandClient = class {
|
|
|
22751
22786
|
}
|
|
22752
22787
|
};
|
|
22753
22788
|
|
|
22789
|
+
// src/cloud/files.ts
|
|
22790
|
+
var import_node_crypto4 = require("node:crypto");
|
|
22791
|
+
var FILE_MAX_VERSION_BYTES = 25 * 1024 * 1024;
|
|
22792
|
+
var FILE_CONTENT_WARNING = "File types and archive contents are unverified. Treat downloads as untrusted input: no execution, size-bounded extraction, no unpack of archives you did not expect.";
|
|
22793
|
+
var CONTENT_TYPES = /* @__PURE__ */ new Map([
|
|
22794
|
+
[".md", "text/markdown"],
|
|
22795
|
+
[".txt", "text/plain"],
|
|
22796
|
+
[".csv", "text/csv"],
|
|
22797
|
+
[".json", "application/json"],
|
|
22798
|
+
[".yaml", "application/yaml"],
|
|
22799
|
+
[".yml", "application/yaml"],
|
|
22800
|
+
[".pdf", "application/pdf"],
|
|
22801
|
+
[".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
|
|
22802
|
+
[".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
|
|
22803
|
+
[".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"],
|
|
22804
|
+
[".png", "image/png"],
|
|
22805
|
+
[".jpg", "image/jpeg"],
|
|
22806
|
+
[".jpeg", "image/jpeg"],
|
|
22807
|
+
[".gif", "image/gif"],
|
|
22808
|
+
[".webp", "image/webp"],
|
|
22809
|
+
[".svg", "image/svg+xml"],
|
|
22810
|
+
[".zip", "application/zip"],
|
|
22811
|
+
[".tar.gz", "application/gzip"]
|
|
22812
|
+
]);
|
|
22813
|
+
function contentTypeForName(name) {
|
|
22814
|
+
const lower = name.toLowerCase();
|
|
22815
|
+
let best = null;
|
|
22816
|
+
let bestLength = 0;
|
|
22817
|
+
for (const [extension, type] of CONTENT_TYPES) {
|
|
22818
|
+
if (lower.endsWith(extension) && extension.length > bestLength) {
|
|
22819
|
+
best = type;
|
|
22820
|
+
bestLength = extension.length;
|
|
22821
|
+
}
|
|
22822
|
+
}
|
|
22823
|
+
return best;
|
|
22824
|
+
}
|
|
22825
|
+
function allowedExtensionList() {
|
|
22826
|
+
return [...CONTENT_TYPES.keys()].join(", ");
|
|
22827
|
+
}
|
|
22828
|
+
var FileCommandRefused = class extends Error {
|
|
22829
|
+
constructor(status, code, message) {
|
|
22830
|
+
super(message);
|
|
22831
|
+
this.status = status;
|
|
22832
|
+
this.code = code;
|
|
22833
|
+
}
|
|
22834
|
+
status;
|
|
22835
|
+
code;
|
|
22836
|
+
name = "FileCommandRefused";
|
|
22837
|
+
};
|
|
22838
|
+
var FileTransportError = class extends Error {
|
|
22839
|
+
/**
|
|
22840
|
+
* True when no HTTP response arrived (connection failure, timeout), so the
|
|
22841
|
+
* outcome is UNKNOWN and one same-id retry is safe under the server's
|
|
22842
|
+
* command-id replay. A received refusal is a known outcome: never retried.
|
|
22843
|
+
*/
|
|
22844
|
+
constructor(message, noResponse = false) {
|
|
22845
|
+
super(message);
|
|
22846
|
+
this.noResponse = noResponse;
|
|
22847
|
+
}
|
|
22848
|
+
noResponse;
|
|
22849
|
+
name = "FileTransportError";
|
|
22850
|
+
};
|
|
22851
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
22852
|
+
async function sendFileCommand(options, command2) {
|
|
22853
|
+
const fetcher = options.fetcher ?? fetch;
|
|
22854
|
+
const controller = new AbortController();
|
|
22855
|
+
const timer2 = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
22856
|
+
let response;
|
|
22857
|
+
try {
|
|
22858
|
+
response = await fetcher(commandEndpoint(options.target), {
|
|
22859
|
+
method: "POST",
|
|
22860
|
+
headers: {
|
|
22861
|
+
authorization: `Bearer ${options.credential}`,
|
|
22862
|
+
apikey: options.target.anonKey,
|
|
22863
|
+
"content-type": "application/json"
|
|
22864
|
+
},
|
|
22865
|
+
body: JSON.stringify({
|
|
22866
|
+
command_id: options.commandId ?? newCommandId(),
|
|
22867
|
+
client_version: "0.1.0",
|
|
22868
|
+
workspace_id: options.workspaceId,
|
|
22869
|
+
stream: { kind: "workspace" },
|
|
22870
|
+
command: command2
|
|
22871
|
+
}),
|
|
22872
|
+
signal: controller.signal
|
|
22873
|
+
});
|
|
22874
|
+
} catch (error) {
|
|
22875
|
+
if (error.name === "AbortError") {
|
|
22876
|
+
throw new FileTransportError("file command timed out", true);
|
|
22877
|
+
}
|
|
22878
|
+
throw new FileTransportError("file command failed before a response", true);
|
|
22879
|
+
} finally {
|
|
22880
|
+
clearTimeout(timer2);
|
|
22881
|
+
}
|
|
22882
|
+
const body = await response.json().catch(() => null);
|
|
22883
|
+
if (!response.ok) {
|
|
22884
|
+
const code = typeof body?.error === "string" ? body.error : "http_error";
|
|
22885
|
+
const message = typeof body?.message === "string" ? body.message : `file command failed (HTTP ${response.status})`;
|
|
22886
|
+
throw new FileCommandRefused(response.status, code, message);
|
|
22887
|
+
}
|
|
22888
|
+
if (!body || typeof body !== "object") {
|
|
22889
|
+
throw new FileTransportError("file command returned a malformed response");
|
|
22890
|
+
}
|
|
22891
|
+
return body;
|
|
22892
|
+
}
|
|
22893
|
+
function fileVersionCreate(options, input) {
|
|
22894
|
+
return sendFileCommand(options, {
|
|
22895
|
+
kind: "file_version_create",
|
|
22896
|
+
file_id: input.fileId,
|
|
22897
|
+
version_id: input.versionId,
|
|
22898
|
+
name: input.name,
|
|
22899
|
+
declared_size_bytes: input.declaredSizeBytes,
|
|
22900
|
+
content_type: input.contentType
|
|
22901
|
+
});
|
|
22902
|
+
}
|
|
22903
|
+
function fileVersionCommit(options, input) {
|
|
22904
|
+
return sendFileCommand(options, {
|
|
22905
|
+
kind: "file_version_commit",
|
|
22906
|
+
file_id: input.fileId,
|
|
22907
|
+
version_id: input.versionId,
|
|
22908
|
+
sha256: input.sha256
|
|
22909
|
+
});
|
|
22910
|
+
}
|
|
22911
|
+
function fileDownloadUrl(options, input) {
|
|
22912
|
+
return sendFileCommand(options, {
|
|
22913
|
+
kind: "file_download_url",
|
|
22914
|
+
file_id: input.fileId,
|
|
22915
|
+
version_n: input.versionN
|
|
22916
|
+
});
|
|
22917
|
+
}
|
|
22918
|
+
function fileTombstone(options, input) {
|
|
22919
|
+
return sendFileCommand(options, {
|
|
22920
|
+
kind: "file_tombstone",
|
|
22921
|
+
file_id: input.fileId
|
|
22922
|
+
});
|
|
22923
|
+
}
|
|
22924
|
+
function fileRestore(options, input) {
|
|
22925
|
+
return sendFileCommand(options, {
|
|
22926
|
+
kind: "file_restore",
|
|
22927
|
+
file_id: input.fileId
|
|
22928
|
+
});
|
|
22929
|
+
}
|
|
22930
|
+
function absoluteStorageUrl(target2, path) {
|
|
22931
|
+
if (!path.startsWith("/")) {
|
|
22932
|
+
throw new FileTransportError(
|
|
22933
|
+
"the server returned a storage path that is not relative; refusing to compose a URL from it"
|
|
22934
|
+
);
|
|
22935
|
+
}
|
|
22936
|
+
return `${target2.url}${path}`;
|
|
22937
|
+
}
|
|
22938
|
+
async function putObject(target2, uploadPath, bytes, contentType, fetcher = fetch) {
|
|
22939
|
+
let response;
|
|
22940
|
+
try {
|
|
22941
|
+
response = await fetcher(absoluteStorageUrl(target2, uploadPath), {
|
|
22942
|
+
method: "PUT",
|
|
22943
|
+
headers: { "content-type": contentType },
|
|
22944
|
+
/* Node's Buffer types as Uint8Array<ArrayBufferLike>, which the DOM-lib
|
|
22945
|
+
* BodyInit rejects since TS 5.7; the runtime accepts it. */
|
|
22946
|
+
body: bytes
|
|
22947
|
+
});
|
|
22948
|
+
} catch {
|
|
22949
|
+
throw new FileTransportError("the upload PUT failed before a response", true);
|
|
22950
|
+
}
|
|
22951
|
+
if (!response.ok) {
|
|
22952
|
+
throw new FileTransportError(
|
|
22953
|
+
`the upload PUT was refused (HTTP ${response.status}). Nothing went live, and this attempt's pending slot expires on its own within three hours. Check cswarm file ls, then re-run cswarm file put \u2014 a re-run is a new upload attempt with fresh ids`
|
|
22954
|
+
);
|
|
22955
|
+
}
|
|
22956
|
+
}
|
|
22957
|
+
async function onceRetried(step) {
|
|
22958
|
+
try {
|
|
22959
|
+
return await step();
|
|
22960
|
+
} catch (error) {
|
|
22961
|
+
if (error instanceof FileTransportError && error.noResponse) {
|
|
22962
|
+
return await step();
|
|
22963
|
+
}
|
|
22964
|
+
throw error;
|
|
22965
|
+
}
|
|
22966
|
+
}
|
|
22967
|
+
async function getObject(target2, downloadPath, fetcher = fetch) {
|
|
22968
|
+
let response;
|
|
22969
|
+
try {
|
|
22970
|
+
response = await fetcher(absoluteStorageUrl(target2, downloadPath));
|
|
22971
|
+
} catch {
|
|
22972
|
+
throw new FileTransportError("the download failed before a response");
|
|
22973
|
+
}
|
|
22974
|
+
if (!response.ok) {
|
|
22975
|
+
throw new FileTransportError(
|
|
22976
|
+
`the download was refused (HTTP ${response.status}); the signed URL lasts five minutes \u2014 request a fresh one with cswarm file get`
|
|
22977
|
+
);
|
|
22978
|
+
}
|
|
22979
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
22980
|
+
}
|
|
22981
|
+
var LocalFileExists = class extends Error {
|
|
22982
|
+
name = "LocalFileExists";
|
|
22983
|
+
};
|
|
22984
|
+
function writeDestination(destination, bytes, force, writer) {
|
|
22985
|
+
try {
|
|
22986
|
+
writer(destination, bytes, { flag: force ? "w" : "wx" });
|
|
22987
|
+
} catch (error) {
|
|
22988
|
+
if (error.code === "EEXIST") {
|
|
22989
|
+
throw new LocalFileExists(
|
|
22990
|
+
`${destination} already exists locally; nothing was written. Pass --force to overwrite it, or --out <path> to write elsewhere`
|
|
22991
|
+
);
|
|
22992
|
+
}
|
|
22993
|
+
throw error;
|
|
22994
|
+
}
|
|
22995
|
+
}
|
|
22996
|
+
function sha256Hex(bytes) {
|
|
22997
|
+
return (0, import_node_crypto4.createHash)("sha256").update(bytes).digest("hex");
|
|
22998
|
+
}
|
|
22999
|
+
async function listFilesAsAgent(target2, credential, workspaceId2, fetcher = fetch) {
|
|
23000
|
+
let response;
|
|
23001
|
+
try {
|
|
23002
|
+
response = await fetcher(readEndpoint(target2), {
|
|
23003
|
+
method: "POST",
|
|
23004
|
+
headers: {
|
|
23005
|
+
authorization: `Bearer ${credential}`,
|
|
23006
|
+
apikey: target2.anonKey,
|
|
23007
|
+
"content-type": "application/json"
|
|
23008
|
+
},
|
|
23009
|
+
body: JSON.stringify({ resource: "files", workspace_id: workspaceId2 })
|
|
23010
|
+
});
|
|
23011
|
+
} catch {
|
|
23012
|
+
throw new FileTransportError("file list could not reach the cloud service");
|
|
23013
|
+
}
|
|
23014
|
+
if (!response.ok) {
|
|
23015
|
+
throw new FileCommandRefused(
|
|
23016
|
+
response.status,
|
|
23017
|
+
"http_error",
|
|
23018
|
+
`file list failed (HTTP ${response.status})`
|
|
23019
|
+
);
|
|
23020
|
+
}
|
|
23021
|
+
const body = await response.json().catch(() => null);
|
|
23022
|
+
if (!body || !Array.isArray(body.files)) {
|
|
23023
|
+
throw new FileTransportError("file list returned a malformed response");
|
|
23024
|
+
}
|
|
23025
|
+
return body.files;
|
|
23026
|
+
}
|
|
23027
|
+
async function listFilesAsHuman(target2, accessToken, workspaceId2, fetcher = fetch) {
|
|
23028
|
+
const url = new URL("/rest/v1/files", target2.url);
|
|
23029
|
+
url.searchParams.set("workspace_id", `eq.${workspaceId2}`);
|
|
23030
|
+
url.searchParams.set(
|
|
23031
|
+
"select",
|
|
23032
|
+
"file_id,name,current_version,size_bytes,content_type,sha256,created_by_kind,created_by,uploaded_by_kind,uploaded_by,created_at,committed_at,tombstoned_at"
|
|
23033
|
+
);
|
|
23034
|
+
url.searchParams.set("order", "name.asc");
|
|
23035
|
+
let response;
|
|
23036
|
+
try {
|
|
23037
|
+
response = await fetcher(url, {
|
|
23038
|
+
headers: {
|
|
23039
|
+
authorization: `Bearer ${accessToken}`,
|
|
23040
|
+
apikey: target2.anonKey,
|
|
23041
|
+
"accept-profile": "swarm_read"
|
|
23042
|
+
}
|
|
23043
|
+
});
|
|
23044
|
+
} catch {
|
|
23045
|
+
throw new FileTransportError("file list could not reach the cloud service");
|
|
23046
|
+
}
|
|
23047
|
+
if (!response.ok) {
|
|
23048
|
+
throw new FileCommandRefused(
|
|
23049
|
+
response.status,
|
|
23050
|
+
"http_error",
|
|
23051
|
+
`file list failed (HTTP ${response.status})`
|
|
23052
|
+
);
|
|
23053
|
+
}
|
|
23054
|
+
const body = await response.json().catch(() => null);
|
|
23055
|
+
if (!Array.isArray(body)) {
|
|
23056
|
+
throw new FileTransportError("file list returned a malformed response");
|
|
23057
|
+
}
|
|
23058
|
+
return body;
|
|
23059
|
+
}
|
|
23060
|
+
|
|
22754
23061
|
// src/cloud/current-target.ts
|
|
22755
|
-
var
|
|
23062
|
+
var import_node_crypto6 = require("node:crypto");
|
|
22756
23063
|
var import_promises3 = require("node:fs/promises");
|
|
22757
23064
|
var import_node_path2 = require("node:path");
|
|
22758
23065
|
|
|
@@ -22761,7 +23068,7 @@ var import_node_fs = require("node:fs");
|
|
|
22761
23068
|
var import_promises = require("node:fs/promises");
|
|
22762
23069
|
var import_node_os = require("node:os");
|
|
22763
23070
|
var import_node_path = require("node:path");
|
|
22764
|
-
var
|
|
23071
|
+
var import_node_crypto5 = require("node:crypto");
|
|
22765
23072
|
var import_node_child_process2 = require("node:child_process");
|
|
22766
23073
|
var import_promises2 = require("node:timers/promises");
|
|
22767
23074
|
var KEYCHAIN_SERVICE = "com.commonswarm.cli";
|
|
@@ -22924,7 +23231,7 @@ async function withFileLock(stateDirectory2, lockName, work) {
|
|
|
22924
23231
|
if (Date.now() >= deadline) {
|
|
22925
23232
|
throw new Error("timed out waiting for the credential refresh lock");
|
|
22926
23233
|
}
|
|
22927
|
-
await (0, import_promises2.setTimeout)(25 + (0,
|
|
23234
|
+
await (0, import_promises2.setTimeout)(25 + (0, import_node_crypto5.randomBytes)(1)[0] % 75);
|
|
22928
23235
|
}
|
|
22929
23236
|
}
|
|
22930
23237
|
try {
|
|
@@ -22941,7 +23248,7 @@ async function writeSecureJsonFile(path, serialized) {
|
|
|
22941
23248
|
} catch (error) {
|
|
22942
23249
|
if (error.code !== "ENOENT") throw error;
|
|
22943
23250
|
}
|
|
22944
|
-
const temporary = `${path}.${process.pid}.${(0,
|
|
23251
|
+
const temporary = `${path}.${process.pid}.${(0, import_node_crypto5.randomBytes)(6).toString("hex")}.tmp`;
|
|
22945
23252
|
const handle = await (0, import_promises.open)(temporary, "wx", 384);
|
|
22946
23253
|
try {
|
|
22947
23254
|
await handle.writeFile(serialized, "utf8");
|
|
@@ -23291,7 +23598,7 @@ async function writeCurrentTarget(target2, options = {}) {
|
|
|
23291
23598
|
anonKey: validated.anonKey
|
|
23292
23599
|
};
|
|
23293
23600
|
const serialized = JSON.stringify(record);
|
|
23294
|
-
const temporary = `${path}.${process.pid}.${(0,
|
|
23601
|
+
const temporary = `${path}.${process.pid}.${(0, import_node_crypto6.randomBytes)(6).toString("hex")}.tmp`;
|
|
23295
23602
|
const handle = await (0, import_promises3.open)(temporary, "wx", 384);
|
|
23296
23603
|
try {
|
|
23297
23604
|
await handle.writeFile(serialized, "utf8");
|
|
@@ -23319,7 +23626,7 @@ async function clearCurrentTarget(options = {}) {
|
|
|
23319
23626
|
}
|
|
23320
23627
|
}
|
|
23321
23628
|
function targetFingerprint(target2) {
|
|
23322
|
-
return (0,
|
|
23629
|
+
return (0, import_node_crypto6.createHash)("sha256").update(target2.anonKey).digest("hex").slice(0, 12);
|
|
23323
23630
|
}
|
|
23324
23631
|
function currentTargetSummary(target2, reveal = false) {
|
|
23325
23632
|
return {
|
|
@@ -23414,7 +23721,7 @@ async function resolveCloudTarget(options) {
|
|
|
23414
23721
|
}
|
|
23415
23722
|
|
|
23416
23723
|
// src/cloud/seed.ts
|
|
23417
|
-
var
|
|
23724
|
+
var import_node_crypto7 = require("node:crypto");
|
|
23418
23725
|
|
|
23419
23726
|
// node_modules/postgres/src/index.js
|
|
23420
23727
|
var import_os = __toESM(require("os"), 1);
|
|
@@ -25552,7 +25859,7 @@ var P0_SCOPES = [
|
|
|
25552
25859
|
"post_signal"
|
|
25553
25860
|
];
|
|
25554
25861
|
function deterministicUuid(label) {
|
|
25555
|
-
const bytes = (0,
|
|
25862
|
+
const bytes = (0, import_node_crypto7.createHash)("sha256").update(label).digest().subarray(0, 16);
|
|
25556
25863
|
bytes[6] = bytes[6] & 15 | 80;
|
|
25557
25864
|
bytes[8] = bytes[8] & 63 | 128;
|
|
25558
25865
|
const hex = bytes.toString("hex");
|
|
@@ -25725,10 +26032,10 @@ async function seedDogfood(options) {
|
|
|
25725
26032
|
tokenExpiresAt: existing[0].expires_at.toISOString()
|
|
25726
26033
|
};
|
|
25727
26034
|
}
|
|
25728
|
-
const agentToken = `swm_agt_${(0,
|
|
25729
|
-
const tokenHash = (0,
|
|
25730
|
-
const tokenId = (0,
|
|
25731
|
-
const lineageId = (0,
|
|
26035
|
+
const agentToken = `swm_agt_${(0, import_node_crypto7.randomBytes)(32).toString("base64url")}`;
|
|
26036
|
+
const tokenHash = (0, import_node_crypto7.createHash)("sha256").update(agentToken).digest();
|
|
26037
|
+
const tokenId = (0, import_node_crypto7.randomUUID)();
|
|
26038
|
+
const lineageId = (0, import_node_crypto7.randomUUID)();
|
|
25732
26039
|
const inserted = await tx`
|
|
25733
26040
|
INSERT INTO swarm.agent_tokens (
|
|
25734
26041
|
token_id, principal_id, run_id, task_id, epoch,
|
|
@@ -25768,14 +26075,14 @@ async function seedDogfood(options) {
|
|
|
25768
26075
|
}
|
|
25769
26076
|
|
|
25770
26077
|
// src/cloud/agent-credential.ts
|
|
25771
|
-
var
|
|
26078
|
+
var import_node_crypto8 = require("node:crypto");
|
|
25772
26079
|
var import_node_os2 = require("node:os");
|
|
25773
26080
|
var import_node_path3 = require("node:path");
|
|
25774
26081
|
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
25775
26082
|
var AGENT_TOKEN_RE2 = /^swm_agt_[A-Za-z0-9_-]{43}$/;
|
|
25776
26083
|
var MAX_RECORD_BYTES = 4 * 1024;
|
|
25777
26084
|
function credentialLineageKey(rootToken) {
|
|
25778
|
-
return (0,
|
|
26085
|
+
return (0, import_node_crypto8.createHash)("sha256").update(rootToken).digest("hex").slice(0, 32);
|
|
25779
26086
|
}
|
|
25780
26087
|
function defaultAgentCredentialDirectory() {
|
|
25781
26088
|
const configured = process.env.SWARM_AGENT_STATE_DIR;
|
|
@@ -25835,7 +26142,7 @@ async function agentCredentialStore(options) {
|
|
|
25835
26142
|
}
|
|
25836
26143
|
|
|
25837
26144
|
// src/cloud/renewal.ts
|
|
25838
|
-
var
|
|
26145
|
+
var import_node_crypto9 = require("node:crypto");
|
|
25839
26146
|
var AGENT_TOKEN_DEFAULT_TTL_MS2 = 60 * 60 * 1e3;
|
|
25840
26147
|
var AGENT_TOKEN_MAX_TTL_MS2 = 8 * 60 * 60 * 1e3;
|
|
25841
26148
|
var RENEWAL_HORIZON_DEFAULT_MS2 = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -25920,7 +26227,7 @@ function renewalCommand() {
|
|
|
25920
26227
|
return command2;
|
|
25921
26228
|
}
|
|
25922
26229
|
function newRenewalCommandId() {
|
|
25923
|
-
return `ren_${(0,
|
|
26230
|
+
return `ren_${(0, import_node_crypto9.randomBytes)(18).toString("base64url")}`;
|
|
25924
26231
|
}
|
|
25925
26232
|
function timestamp(value) {
|
|
25926
26233
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
@@ -26406,11 +26713,11 @@ var AgentCredentialSession = class _AgentCredentialSession {
|
|
|
26406
26713
|
};
|
|
26407
26714
|
|
|
26408
26715
|
// src/cloud/pending-command.ts
|
|
26409
|
-
var
|
|
26716
|
+
var import_node_crypto10 = require("node:crypto");
|
|
26410
26717
|
var MAX_PENDING_COMMANDS2 = 32;
|
|
26411
26718
|
var SIGNAL_PENDING_RECOVERY_MS = 60 * 60 * 1e3;
|
|
26412
26719
|
function intentHash(workspace, command2) {
|
|
26413
|
-
return (0,
|
|
26720
|
+
return (0, import_node_crypto10.createHash)("sha256").update(canonicalJson({ workspace_id: workspace ?? null, command: command2 })).digest("hex");
|
|
26414
26721
|
}
|
|
26415
26722
|
async function pendingCommandId(credentials, userId, workspace, command2) {
|
|
26416
26723
|
const intent = intentHash(workspace, command2);
|
|
@@ -26510,7 +26817,7 @@ async function sendCapabilityWithPending(client, session, workspace, command2) {
|
|
|
26510
26817
|
}
|
|
26511
26818
|
}
|
|
26512
26819
|
function signalIntentHash(workspace, command2, credentialIdentity) {
|
|
26513
|
-
return (0,
|
|
26820
|
+
return (0, import_node_crypto10.createHash)("sha256").update(canonicalJson({
|
|
26514
26821
|
workspace_id: workspace,
|
|
26515
26822
|
command: command2,
|
|
26516
26823
|
credential_identity: credentialIdentity
|
|
@@ -26592,7 +26899,7 @@ async function sendSignalWithPending(client, session, workspace, command2) {
|
|
|
26592
26899
|
var import_node_os3 = require("node:os");
|
|
26593
26900
|
|
|
26594
26901
|
// src/cloud/invite-link.ts
|
|
26595
|
-
var
|
|
26902
|
+
var import_node_crypto11 = require("node:crypto");
|
|
26596
26903
|
var MAX_LINK_PAYLOAD_BYTES = 8 * 1024;
|
|
26597
26904
|
var MAX_LABEL_INPUT_LENGTH = 1024;
|
|
26598
26905
|
var CONTROL_GLOBAL_RE = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g;
|
|
@@ -26763,7 +27070,7 @@ function equalExact(actual, expected) {
|
|
|
26763
27070
|
const paddedRight = Buffer.alloc(length);
|
|
26764
27071
|
left.copy(paddedLeft);
|
|
26765
27072
|
right.copy(paddedRight);
|
|
26766
|
-
const equal = (0,
|
|
27073
|
+
const equal = (0, import_node_crypto11.timingSafeEqual)(paddedLeft, paddedRight);
|
|
26767
27074
|
return equal && left.length === right.length;
|
|
26768
27075
|
}
|
|
26769
27076
|
async function requirePinnedOrigin(target2, options) {
|
|
@@ -29065,7 +29372,7 @@ async function runInboxFollow(options) {
|
|
|
29065
29372
|
|
|
29066
29373
|
// src/host/opencode.ts
|
|
29067
29374
|
var import_node_child_process3 = require("node:child_process");
|
|
29068
|
-
var
|
|
29375
|
+
var import_node_crypto12 = require("node:crypto");
|
|
29069
29376
|
var import_node_fs3 = require("node:fs");
|
|
29070
29377
|
var import_promises4 = require("node:fs/promises");
|
|
29071
29378
|
var import_node_os4 = require("node:os");
|
|
@@ -30250,7 +30557,7 @@ function buildOpenCodeHomeOwner(options) {
|
|
|
30250
30557
|
version: 1,
|
|
30251
30558
|
pid: options.pid ?? process.pid,
|
|
30252
30559
|
uid: uid2,
|
|
30253
|
-
instanceId: options.instanceId ?? (0,
|
|
30560
|
+
instanceId: options.instanceId ?? (0, import_node_crypto12.randomUUID)(),
|
|
30254
30561
|
role: options.role,
|
|
30255
30562
|
createdAt: new Date((options.now ?? Date.now)()).toISOString()
|
|
30256
30563
|
};
|
|
@@ -31798,7 +32105,7 @@ var ListenerEngine = class {
|
|
|
31798
32105
|
};
|
|
31799
32106
|
|
|
31800
32107
|
// src/listener/file-store.ts
|
|
31801
|
-
var
|
|
32108
|
+
var import_node_crypto13 = require("node:crypto");
|
|
31802
32109
|
var import_node_os5 = require("node:os");
|
|
31803
32110
|
var import_node_path8 = require("node:path");
|
|
31804
32111
|
var import_node_util = require("node:util");
|
|
@@ -31845,7 +32152,7 @@ function listenerInstanceKey(input) {
|
|
|
31845
32152
|
if (!input.profileId || input.profileId.includes("\0")) {
|
|
31846
32153
|
throw new Error("listener profile id is invalid");
|
|
31847
32154
|
}
|
|
31848
|
-
return (0,
|
|
32155
|
+
return (0, import_node_crypto13.createHash)("sha256").update(input.profileId).update("\0").update(input.workspaceId.toLowerCase()).update("\0").update(input.principalId.toLowerCase()).digest("hex");
|
|
31849
32156
|
}
|
|
31850
32157
|
function integer(value) {
|
|
31851
32158
|
return Number.isSafeInteger(value) && value >= 0;
|
|
@@ -32380,7 +32687,7 @@ var GrokListenerModel = class {
|
|
|
32380
32687
|
};
|
|
32381
32688
|
|
|
32382
32689
|
// src/listener/opencode-model.ts
|
|
32383
|
-
var
|
|
32690
|
+
var import_node_crypto14 = require("node:crypto");
|
|
32384
32691
|
var import_promises6 = require("node:fs/promises");
|
|
32385
32692
|
var import_node_path11 = require("node:path");
|
|
32386
32693
|
function asError(error) {
|
|
@@ -32406,7 +32713,7 @@ var OpenCodeListenerModel = class {
|
|
|
32406
32713
|
prepareWorkerCwd;
|
|
32407
32714
|
permissionMode;
|
|
32408
32715
|
pendingOpenWaitMs;
|
|
32409
|
-
instanceId = (0,
|
|
32716
|
+
instanceId = (0, import_node_crypto14.randomUUID)();
|
|
32410
32717
|
worker = null;
|
|
32411
32718
|
workerHome = null;
|
|
32412
32719
|
/** Worker homes retained after failed close or unsettled open. */
|
|
@@ -32810,7 +33117,7 @@ var OpenCodeListenerModel = class {
|
|
|
32810
33117
|
};
|
|
32811
33118
|
|
|
32812
33119
|
// src/listener/claude-model.ts
|
|
32813
|
-
var
|
|
33120
|
+
var import_node_crypto15 = require("node:crypto");
|
|
32814
33121
|
var import_promises7 = require("node:fs/promises");
|
|
32815
33122
|
var import_node_os7 = require("node:os");
|
|
32816
33123
|
var import_node_path12 = require("node:path");
|
|
@@ -32950,7 +33257,7 @@ var ClaudeListenerModel = class {
|
|
|
32950
33257
|
async enablePromptsAfterClaudeCanary(handle) {
|
|
32951
33258
|
const sentinelPath = (0, import_node_path12.join)(
|
|
32952
33259
|
(0, import_node_os7.tmpdir)(),
|
|
32953
|
-
`cswarm-claude-permission-canary-${process.pid}-${(0,
|
|
33260
|
+
`cswarm-claude-permission-canary-${process.pid}-${(0, import_node_crypto15.randomUUID)()}`
|
|
32954
33261
|
);
|
|
32955
33262
|
let sentinelCreated = false;
|
|
32956
33263
|
try {
|
|
@@ -32976,7 +33283,7 @@ var ClaudeListenerModel = class {
|
|
|
32976
33283
|
};
|
|
32977
33284
|
|
|
32978
33285
|
// src/listener/codex-model.ts
|
|
32979
|
-
var
|
|
33286
|
+
var import_node_crypto16 = require("node:crypto");
|
|
32980
33287
|
var import_promises8 = require("node:fs/promises");
|
|
32981
33288
|
var import_node_os8 = require("node:os");
|
|
32982
33289
|
var import_node_path13 = require("node:path");
|
|
@@ -33116,7 +33423,7 @@ var CodexListenerModel = class {
|
|
|
33116
33423
|
async enablePromptsAfterCodexCanary(handle) {
|
|
33117
33424
|
const sentinelPath = (0, import_node_path13.join)(
|
|
33118
33425
|
(0, import_node_os8.tmpdir)(),
|
|
33119
|
-
`cswarm-codex-permission-canary-${process.pid}-${(0,
|
|
33426
|
+
`cswarm-codex-permission-canary-${process.pid}-${(0, import_node_crypto16.randomUUID)()}`
|
|
33120
33427
|
);
|
|
33121
33428
|
let sentinelCreated = false;
|
|
33122
33429
|
try {
|
|
@@ -33142,7 +33449,7 @@ var CodexListenerModel = class {
|
|
|
33142
33449
|
};
|
|
33143
33450
|
|
|
33144
33451
|
// src/listener/runtime.ts
|
|
33145
|
-
var
|
|
33452
|
+
var import_node_crypto17 = require("node:crypto");
|
|
33146
33453
|
|
|
33147
33454
|
// src/cloud/delivery.ts
|
|
33148
33455
|
var UUID_RE10 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -33873,7 +34180,7 @@ function sameEffectSignal(record, signal) {
|
|
|
33873
34180
|
return record.signalId === signal.id.toLowerCase() && record.signalKind === signal.kind && record.askBody === signal.body && record.askUntil === signal.until && record.senderOwnerRelation === (signal.sender_owner_relation ?? "unknown");
|
|
33874
34181
|
}
|
|
33875
34182
|
function immutableSignalFingerprint(signalId, signalKind2, body, until, senderOwnerRelation) {
|
|
33876
|
-
return (0,
|
|
34183
|
+
return (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify([
|
|
33877
34184
|
signalId,
|
|
33878
34185
|
signalKind2,
|
|
33879
34186
|
body,
|
|
@@ -34214,6 +34521,30 @@ async function runListenerRuntime(options) {
|
|
|
34214
34521
|
principalId: options.principalId,
|
|
34215
34522
|
ts: eventTime(now)
|
|
34216
34523
|
});
|
|
34524
|
+
if (options.declareModel !== void 0) {
|
|
34525
|
+
const declaredLabel = options.declareModel;
|
|
34526
|
+
void (async () => {
|
|
34527
|
+
let declared = false;
|
|
34528
|
+
try {
|
|
34529
|
+
const credential = await options.credentialSession.bearer();
|
|
34530
|
+
const outcome = await declareAgentModel(options.target, {
|
|
34531
|
+
workspaceId: options.workspaceId,
|
|
34532
|
+
model: declaredLabel,
|
|
34533
|
+
credential,
|
|
34534
|
+
...abort ? { signal: abort } : {}
|
|
34535
|
+
}, options.fetcher);
|
|
34536
|
+
declared = outcome.httpStatus === 200;
|
|
34537
|
+
} catch {
|
|
34538
|
+
declared = false;
|
|
34539
|
+
}
|
|
34540
|
+
options.onEvent?.({
|
|
34541
|
+
type: "model_declared",
|
|
34542
|
+
ok: declared,
|
|
34543
|
+
model: declaredLabel,
|
|
34544
|
+
ts: eventTime(now)
|
|
34545
|
+
});
|
|
34546
|
+
})();
|
|
34547
|
+
}
|
|
34217
34548
|
}
|
|
34218
34549
|
let currentJournalRecord = null;
|
|
34219
34550
|
if (durableConfigured) {
|
|
@@ -35068,7 +35399,7 @@ async function queryListenerControl(paths, command2, timeoutMs = CONTROL_TIMEOUT
|
|
|
35068
35399
|
}
|
|
35069
35400
|
|
|
35070
35401
|
// src/listener/supervisor.ts
|
|
35071
|
-
var
|
|
35402
|
+
var import_node_crypto18 = require("node:crypto");
|
|
35072
35403
|
var UUID_RE13 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
35073
35404
|
var LISTENER_RESTART_MAX_ATTEMPTS = 5;
|
|
35074
35405
|
var LISTENER_RESTART_INITIAL_MS = 1e3;
|
|
@@ -35116,7 +35447,7 @@ async function runListenerSupervisor(options) {
|
|
|
35116
35447
|
const now = options.now ?? Date.now;
|
|
35117
35448
|
const startedAt = iso2(now);
|
|
35118
35449
|
const controller = new AbortController();
|
|
35119
|
-
const proposedInstanceId = (0,
|
|
35450
|
+
const proposedInstanceId = (0, import_node_crypto18.randomUUID)();
|
|
35120
35451
|
let status = {
|
|
35121
35452
|
version: 1,
|
|
35122
35453
|
instanceId: proposedInstanceId,
|
|
@@ -35227,6 +35558,15 @@ async function runListenerSupervisor(options) {
|
|
|
35227
35558
|
});
|
|
35228
35559
|
return;
|
|
35229
35560
|
}
|
|
35561
|
+
if (event.type === "model_declared") {
|
|
35562
|
+
log({
|
|
35563
|
+
ts: event.ts,
|
|
35564
|
+
event: "listener_model_declared",
|
|
35565
|
+
ok: event.ok,
|
|
35566
|
+
model: event.model
|
|
35567
|
+
});
|
|
35568
|
+
return;
|
|
35569
|
+
}
|
|
35230
35570
|
if (event.type === "delivery_mode") {
|
|
35231
35571
|
status = {
|
|
35232
35572
|
...status,
|
|
@@ -36276,12 +36616,14 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
36276
36616
|
"epoch",
|
|
36277
36617
|
"evidence",
|
|
36278
36618
|
"follow",
|
|
36619
|
+
"force",
|
|
36279
36620
|
"force-file-store",
|
|
36280
36621
|
"foreground",
|
|
36281
36622
|
"grok-executable",
|
|
36282
36623
|
"head-sha",
|
|
36283
36624
|
"help",
|
|
36284
36625
|
"include-stale",
|
|
36626
|
+
"include-tombstoned",
|
|
36285
36627
|
"invitation-id",
|
|
36286
36628
|
"invitation-token-stdin",
|
|
36287
36629
|
"json",
|
|
@@ -36294,6 +36636,7 @@ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
36294
36636
|
"ndjson",
|
|
36295
36637
|
"no-browser",
|
|
36296
36638
|
"opencode-executable",
|
|
36639
|
+
"out",
|
|
36297
36640
|
"permissions",
|
|
36298
36641
|
"principal-id",
|
|
36299
36642
|
"provider",
|
|
@@ -36318,9 +36661,11 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
36318
36661
|
"all-devices",
|
|
36319
36662
|
"force-file-store",
|
|
36320
36663
|
"follow",
|
|
36664
|
+
"force",
|
|
36321
36665
|
"foreground",
|
|
36322
36666
|
"help",
|
|
36323
36667
|
"include-stale",
|
|
36668
|
+
"include-tombstoned",
|
|
36324
36669
|
"invitation-token-stdin",
|
|
36325
36670
|
"json",
|
|
36326
36671
|
"link-stdin",
|
|
@@ -36337,8 +36682,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
36337
36682
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
36338
36683
|
];
|
|
36339
36684
|
function packageVersion() {
|
|
36340
|
-
if ("0.1.
|
|
36341
|
-
return "0.1.
|
|
36685
|
+
if ("0.1.20".length > 0) {
|
|
36686
|
+
return "0.1.20";
|
|
36342
36687
|
}
|
|
36343
36688
|
try {
|
|
36344
36689
|
const value = JSON.parse(
|
|
@@ -36460,6 +36805,11 @@ Usage:
|
|
|
36460
36805
|
cswarm feed [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--about <ref>] [--kind <kind>] [--since <timestamp>] [--limit <n>] [--include-stale] [--json]
|
|
36461
36806
|
cswarm inbox [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale] [--wait <seconds>] [--json]
|
|
36462
36807
|
cswarm inbox --follow --ndjson [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--kind <kind>] [--about <ref>] [--since <timestamp>] [--limit <n>] [--include-stale]
|
|
36808
|
+
cswarm file put <local-path> [--name <name>] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
36809
|
+
cswarm file ls [--include-tombstoned] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
36810
|
+
cswarm file get <name|file-id> [--version <n>] [--out <local-path>] [--force] [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
36811
|
+
cswarm file rm <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
36812
|
+
cswarm file restore <name|file-id> [--url <url> --anon-key <key>] [--workspace-id <uuid>] [--agent-token-stdin] [--json]
|
|
36463
36813
|
cswarm listen start --agent-token-stdin [--url <url> --anon-key <key>] --workspace-id <uuid> --provider grok|opencode|claude|codex [--cwd <absolute-path>] [--model <model>] [--effort <level>] [--permissions deny|allow] [--grok-executable <path>] [--opencode-executable <path>] [--claude-executable <path>] [--codex-executable <path>] [--foreground] [--json]
|
|
36464
36814
|
cswarm listen status [--url <url> --anon-key <key>] --workspace-id <uuid> --principal-id <uuid> [--json]
|
|
36465
36815
|
cswarm listen stop [--url <url> --anon-key <key>] --workspace-id <uuid> --principal-id <uuid> [--json]
|
|
@@ -36493,6 +36843,8 @@ Credential selection for command/dogfood:
|
|
|
36493
36843
|
One that persists or references the credential needs the complete
|
|
36494
36844
|
JSON artifact, because it needs a field a bare secret does not carry:
|
|
36495
36845
|
members reads only -- either form
|
|
36846
|
+
file put, file ls, file get, file rm, file restore
|
|
36847
|
+
read and command, nothing persisted -- either form
|
|
36496
36848
|
listen start persists durable state, rotates -- needs expires_at
|
|
36497
36849
|
token revoke names what it revokes -- needs token_id
|
|
36498
36850
|
|
|
@@ -36835,7 +37187,7 @@ async function runNew(args) {
|
|
|
36835
37187
|
assertWorkspaceName(name);
|
|
36836
37188
|
const cloud = await target(args);
|
|
36837
37189
|
const human = await humanCredential(args, cloud);
|
|
36838
|
-
const proposedId = (0,
|
|
37190
|
+
const proposedId = (0, import_node_crypto19.randomUUID)();
|
|
36839
37191
|
let result;
|
|
36840
37192
|
try {
|
|
36841
37193
|
result = await new ThinCommandClient(cloud).sendConnect({
|
|
@@ -37442,7 +37794,7 @@ async function runToken(args) {
|
|
|
37442
37794
|
...ttl === void 0 ? {} : {
|
|
37443
37795
|
ttl_ms: integer2(args, "ttl-ms", {
|
|
37444
37796
|
minimum: 1,
|
|
37445
|
-
maximum:
|
|
37797
|
+
maximum: 864e5
|
|
37446
37798
|
})
|
|
37447
37799
|
}
|
|
37448
37800
|
}
|
|
@@ -38464,6 +38816,18 @@ function listenerStateDirectory(args) {
|
|
|
38464
38816
|
}
|
|
38465
38817
|
return value;
|
|
38466
38818
|
}
|
|
38819
|
+
function listenerModelLabel(provider) {
|
|
38820
|
+
switch (provider) {
|
|
38821
|
+
case "claude":
|
|
38822
|
+
return "claude (claude-agent-acp 0.64.2)";
|
|
38823
|
+
case "codex":
|
|
38824
|
+
return "codex (codex-acp 1.1.9)";
|
|
38825
|
+
case "opencode":
|
|
38826
|
+
return "opencode";
|
|
38827
|
+
case "grok":
|
|
38828
|
+
return "grok";
|
|
38829
|
+
}
|
|
38830
|
+
}
|
|
38467
38831
|
function listenerProvider(args) {
|
|
38468
38832
|
const provider = args.optional("provider");
|
|
38469
38833
|
const hints = "supported providers: grok \u2014 install Grok CLI 0.2.117 and run grok login; opencode \u2014 install OpenCode 1.18.10 and authenticate it; claude \u2014 npm install -g @agentclientprotocol/claude-agent-acp@0.64.2; codex \u2014 npm install -g @agentclientprotocol/codex-acp@1.1.9. You can use working-on, note, ask, and feed now; detached live receipt needs one of these adapters";
|
|
@@ -38867,6 +39231,7 @@ async function runConfiguredListener(options) {
|
|
|
38867
39231
|
model: newModel(),
|
|
38868
39232
|
signal,
|
|
38869
39233
|
onEvent,
|
|
39234
|
+
declareModel: listenerModelLabel(options.provider),
|
|
38870
39235
|
listenerInstanceId,
|
|
38871
39236
|
deliveryJournal: selectedJournal,
|
|
38872
39237
|
resolveSenderProvenance
|
|
@@ -39123,6 +39488,238 @@ async function runListen(args) {
|
|
|
39123
39488
|
}
|
|
39124
39489
|
throw new UsageError("listen requires start, status, or stop");
|
|
39125
39490
|
}
|
|
39491
|
+
function formatFileSize(value) {
|
|
39492
|
+
const bytes = Number(value ?? 0);
|
|
39493
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
39494
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
39495
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
39496
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
39497
|
+
}
|
|
39498
|
+
async function fileContext(args, extraFlags, positionalCount) {
|
|
39499
|
+
args.assertShape(
|
|
39500
|
+
[...TARGET_FLAGS, "workspace-id", ...CREDENTIAL_FLAGS, "json", ...extraFlags],
|
|
39501
|
+
positionalCount
|
|
39502
|
+
);
|
|
39503
|
+
const cloud = await target(args);
|
|
39504
|
+
const selected = await commandWorkspaceAndCredential(args, cloud, {
|
|
39505
|
+
validateHumanWorkspace: true
|
|
39506
|
+
});
|
|
39507
|
+
return { cloud, selected };
|
|
39508
|
+
}
|
|
39509
|
+
async function fileRows(context) {
|
|
39510
|
+
const { cloud, selected } = context;
|
|
39511
|
+
if (selected.kind === "agent") {
|
|
39512
|
+
return await listFilesAsAgent(
|
|
39513
|
+
cloud,
|
|
39514
|
+
selected.bearer,
|
|
39515
|
+
selected.selectedWorkspace
|
|
39516
|
+
);
|
|
39517
|
+
}
|
|
39518
|
+
return await listFilesAsHuman(
|
|
39519
|
+
cloud,
|
|
39520
|
+
selected.human.accessToken,
|
|
39521
|
+
selected.selectedWorkspace
|
|
39522
|
+
);
|
|
39523
|
+
}
|
|
39524
|
+
async function resolveFileSelector(context, selector) {
|
|
39525
|
+
if (UUID_RE15.test(selector)) return selector.toLowerCase();
|
|
39526
|
+
const rows3 = await fileRows(context);
|
|
39527
|
+
const match = rows3.find(
|
|
39528
|
+
(row) => row.name.toLowerCase() === selector.toLowerCase()
|
|
39529
|
+
);
|
|
39530
|
+
if (match === void 0) {
|
|
39531
|
+
throw new Error(
|
|
39532
|
+
`no file named "${sanitizeDisplayLabel(selector, "that name")}" exists in this workspace; run cswarm file ls to see what does, or pass a file id`
|
|
39533
|
+
);
|
|
39534
|
+
}
|
|
39535
|
+
return match.file_id;
|
|
39536
|
+
}
|
|
39537
|
+
async function runFilePut(args) {
|
|
39538
|
+
const localPath = args.positionals[2];
|
|
39539
|
+
if (!localPath) throw new UsageError("cswarm file put needs a local path");
|
|
39540
|
+
const context = await fileContext(args, ["name"], 3);
|
|
39541
|
+
let bytes;
|
|
39542
|
+
try {
|
|
39543
|
+
bytes = (0, import_node_fs7.readFileSync)(localPath);
|
|
39544
|
+
} catch {
|
|
39545
|
+
throw new Error(`could not read ${localPath}; check the path and permissions`);
|
|
39546
|
+
}
|
|
39547
|
+
const name = args.optional("name") ?? (0, import_node_path17.basename)(localPath);
|
|
39548
|
+
if (bytes.byteLength > FILE_MAX_VERSION_BYTES) {
|
|
39549
|
+
throw new Error(
|
|
39550
|
+
`this file is ${formatFileSize(bytes.byteLength)}; the per-file limit is ${formatFileSize(FILE_MAX_VERSION_BYTES)}, so the upload was not started`
|
|
39551
|
+
);
|
|
39552
|
+
}
|
|
39553
|
+
const contentType = contentTypeForName(name);
|
|
39554
|
+
if (contentType === null) {
|
|
39555
|
+
throw new Error(
|
|
39556
|
+
`"${sanitizeDisplayLabel(name, "that name")}" has no allowed file extension; the workspace accepts ${allowedExtensionList()}`
|
|
39557
|
+
);
|
|
39558
|
+
}
|
|
39559
|
+
const send = {
|
|
39560
|
+
target: context.cloud,
|
|
39561
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
39562
|
+
credential: context.selected.bearer
|
|
39563
|
+
};
|
|
39564
|
+
const fileId = (0, import_node_crypto19.randomUUID)();
|
|
39565
|
+
const versionId = (0, import_node_crypto19.randomUUID)();
|
|
39566
|
+
const createCommandId = newCommandId();
|
|
39567
|
+
const commitCommandId = newCommandId();
|
|
39568
|
+
const created = await onceRetried(
|
|
39569
|
+
() => fileVersionCreate({ ...send, commandId: createCommandId }, {
|
|
39570
|
+
fileId,
|
|
39571
|
+
versionId,
|
|
39572
|
+
name,
|
|
39573
|
+
declaredSizeBytes: bytes.byteLength,
|
|
39574
|
+
contentType
|
|
39575
|
+
})
|
|
39576
|
+
);
|
|
39577
|
+
await onceRetried(
|
|
39578
|
+
() => putObject(context.cloud, created.upload_path, bytes, contentType)
|
|
39579
|
+
);
|
|
39580
|
+
const committed = await onceRetried(
|
|
39581
|
+
() => fileVersionCommit({ ...send, commandId: commitCommandId }, {
|
|
39582
|
+
fileId: created.file_id,
|
|
39583
|
+
versionId: created.version_id,
|
|
39584
|
+
sha256: sha256Hex(bytes)
|
|
39585
|
+
})
|
|
39586
|
+
);
|
|
39587
|
+
if (args.has("json")) {
|
|
39588
|
+
process.stdout.write(`${JSON.stringify(committed, null, 2)}
|
|
39589
|
+
`);
|
|
39590
|
+
return;
|
|
39591
|
+
}
|
|
39592
|
+
process.stdout.write(
|
|
39593
|
+
`Uploaded ${committed.name} \u2014 version ${committed.version_n}, ${formatFileSize(committed.size_bytes)}, visible to everyone in this workspace.
|
|
39594
|
+
Reference for signals, pinned to this version: --about ${committed.reference}
|
|
39595
|
+
The recorded sha256 is an unverified client attestation.
|
|
39596
|
+
`
|
|
39597
|
+
);
|
|
39598
|
+
}
|
|
39599
|
+
async function runFileLs(args) {
|
|
39600
|
+
const context = await fileContext(args, ["include-tombstoned"], 2);
|
|
39601
|
+
const rows3 = await fileRows(context);
|
|
39602
|
+
const visible = args.has("include-tombstoned") ? rows3 : rows3.filter((row) => row.tombstoned_at === null);
|
|
39603
|
+
if (args.has("json")) {
|
|
39604
|
+
process.stdout.write(
|
|
39605
|
+
`${JSON.stringify(
|
|
39606
|
+
{
|
|
39607
|
+
workspace_id: context.selected.selectedWorkspace,
|
|
39608
|
+
files: visible,
|
|
39609
|
+
sha256_note: "unverified client attestation",
|
|
39610
|
+
content_warning: FILE_CONTENT_WARNING
|
|
39611
|
+
},
|
|
39612
|
+
null,
|
|
39613
|
+
2
|
|
39614
|
+
)}
|
|
39615
|
+
`
|
|
39616
|
+
);
|
|
39617
|
+
return;
|
|
39618
|
+
}
|
|
39619
|
+
if (visible.length === 0) {
|
|
39620
|
+
process.stdout.write(
|
|
39621
|
+
rows3.length === 0 ? "No files in this workspace yet. Upload one with cswarm file put <path>.\n" : "No live files; tombstoned ones exist. See them with cswarm file ls --include-tombstoned.\n"
|
|
39622
|
+
);
|
|
39623
|
+
return;
|
|
39624
|
+
}
|
|
39625
|
+
process.stdout.write(`Files in this workspace (${visible.length}):
|
|
39626
|
+
`);
|
|
39627
|
+
for (const row of visible) {
|
|
39628
|
+
const marker = row.tombstoned_at === null ? "" : " [tombstoned; restorable with cswarm file restore]";
|
|
39629
|
+
process.stdout.write(
|
|
39630
|
+
`- ${sanitizeDisplayLabel(row.name, "unnamed file")} v${row.current_version} \xB7 ${formatFileSize(row.size_bytes)} \xB7 ${sanitizeDisplayLabel(row.content_type ?? "unknown type", "unknown type")} \xB7 by ${row.uploaded_by_kind ?? row.created_by_kind}${marker}
|
|
39631
|
+
`
|
|
39632
|
+
);
|
|
39633
|
+
}
|
|
39634
|
+
process.stdout.write(`${FILE_CONTENT_WARNING}
|
|
39635
|
+
`);
|
|
39636
|
+
}
|
|
39637
|
+
async function runFileGet(args) {
|
|
39638
|
+
const selector = args.positionals[2];
|
|
39639
|
+
if (!selector) throw new UsageError("cswarm file get needs a file name or id");
|
|
39640
|
+
const context = await fileContext(args, ["version", "out", "force"], 3);
|
|
39641
|
+
const versionN = args.has("version") ? integer2(args, "version", { minimum: 1 }) : null;
|
|
39642
|
+
const fileId = await resolveFileSelector(context, selector);
|
|
39643
|
+
const send = {
|
|
39644
|
+
target: context.cloud,
|
|
39645
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
39646
|
+
credential: context.selected.bearer
|
|
39647
|
+
};
|
|
39648
|
+
const grant = await fileDownloadUrl(send, { fileId, versionN });
|
|
39649
|
+
const destination = args.optional("out") ?? (0, import_node_path17.basename)(grant.name);
|
|
39650
|
+
const bytes = await getObject(context.cloud, grant.download_path);
|
|
39651
|
+
writeDestination(destination, bytes, args.has("force"), import_node_fs7.writeFileSync);
|
|
39652
|
+
if (args.has("json")) {
|
|
39653
|
+
process.stdout.write(
|
|
39654
|
+
`${JSON.stringify(
|
|
39655
|
+
{ ...grant, written_to: destination, written_bytes: bytes.byteLength },
|
|
39656
|
+
null,
|
|
39657
|
+
2
|
|
39658
|
+
)}
|
|
39659
|
+
`
|
|
39660
|
+
);
|
|
39661
|
+
return;
|
|
39662
|
+
}
|
|
39663
|
+
process.stdout.write(
|
|
39664
|
+
`Downloaded ${grant.name} version ${grant.version_n} (${formatFileSize(bytes.byteLength)}, ${grant.content_type}) to ${destination}.
|
|
39665
|
+
${grant.content_warning}
|
|
39666
|
+
`
|
|
39667
|
+
);
|
|
39668
|
+
}
|
|
39669
|
+
async function runFileRm(args) {
|
|
39670
|
+
const selector = args.positionals[2];
|
|
39671
|
+
if (!selector) throw new UsageError("cswarm file rm needs a file name or id");
|
|
39672
|
+
const context = await fileContext(args, [], 3);
|
|
39673
|
+
const fileId = await resolveFileSelector(context, selector);
|
|
39674
|
+
const result = await fileTombstone({
|
|
39675
|
+
target: context.cloud,
|
|
39676
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
39677
|
+
credential: context.selected.bearer
|
|
39678
|
+
}, { fileId });
|
|
39679
|
+
if (args.has("json")) {
|
|
39680
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
39681
|
+
`);
|
|
39682
|
+
return;
|
|
39683
|
+
}
|
|
39684
|
+
process.stdout.write(
|
|
39685
|
+
`Tombstoned ${result.name}. It is hidden from listings and downloads now, and stays restorable with cswarm file restore until ${result.restorable_until ?? "the 30-day window ends"}.
|
|
39686
|
+
After that the purge permanently deletes the bytes; existing download URLs expire on their own 5-minute clock.
|
|
39687
|
+
`
|
|
39688
|
+
);
|
|
39689
|
+
}
|
|
39690
|
+
async function runFileRestore(args) {
|
|
39691
|
+
const selector = args.positionals[2];
|
|
39692
|
+
if (!selector) {
|
|
39693
|
+
throw new UsageError("cswarm file restore needs a file name or id");
|
|
39694
|
+
}
|
|
39695
|
+
const context = await fileContext(args, [], 3);
|
|
39696
|
+
const fileId = await resolveFileSelector(context, selector);
|
|
39697
|
+
const result = await fileRestore({
|
|
39698
|
+
target: context.cloud,
|
|
39699
|
+
workspaceId: context.selected.selectedWorkspace,
|
|
39700
|
+
credential: context.selected.bearer
|
|
39701
|
+
}, { fileId });
|
|
39702
|
+
if (args.has("json")) {
|
|
39703
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
39704
|
+
`);
|
|
39705
|
+
return;
|
|
39706
|
+
}
|
|
39707
|
+
process.stdout.write(
|
|
39708
|
+
`Restored ${result.name}. It is listed and downloadable again; nothing else changed.
|
|
39709
|
+
`
|
|
39710
|
+
);
|
|
39711
|
+
}
|
|
39712
|
+
async function runFile(args) {
|
|
39713
|
+
const action = args.positionals[1];
|
|
39714
|
+
if (action === "put") return await runFilePut(args);
|
|
39715
|
+
if (action === "ls") return await runFileLs(args);
|
|
39716
|
+
if (action === "get") return await runFileGet(args);
|
|
39717
|
+
if (action === "rm") return await runFileRm(args);
|
|
39718
|
+
if (action === "restore") return await runFileRestore(args);
|
|
39719
|
+
throw new UsageError(
|
|
39720
|
+
"cswarm file takes put, ls, get, rm, or restore"
|
|
39721
|
+
);
|
|
39722
|
+
}
|
|
39126
39723
|
async function runTaskCommand(args) {
|
|
39127
39724
|
args.assertShape(
|
|
39128
39725
|
[...TARGET_FLAGS, ...ROUTE_FLAGS, ...CREDENTIAL_FLAGS, ...TASK_FLAGS],
|
|
@@ -39160,7 +39757,7 @@ async function runDogfood(args) {
|
|
|
39160
39757
|
const { selectedWorkspace, bearer } = await commandWorkspaceAndCredential(args, cloud);
|
|
39161
39758
|
const client = new ThinCommandClient(cloud);
|
|
39162
39759
|
const route = stream(args);
|
|
39163
|
-
const taskId = args.optional("task-id") ?? (0,
|
|
39760
|
+
const taskId = args.optional("task-id") ?? (0, import_node_crypto19.randomUUID)();
|
|
39164
39761
|
const ttl = Number(args.optional("ttl-ms") ?? "3600000");
|
|
39165
39762
|
if (!Number.isSafeInteger(ttl) || ttl <= 0 || ttl > 144e5) {
|
|
39166
39763
|
throw new Error("--ttl-ms must be an integer in 1..14400000");
|
|
@@ -39367,6 +39964,10 @@ async function main() {
|
|
|
39367
39964
|
await runStatus(args);
|
|
39368
39965
|
return;
|
|
39369
39966
|
}
|
|
39967
|
+
if (verb === "file") {
|
|
39968
|
+
await runFile(args);
|
|
39969
|
+
return;
|
|
39970
|
+
}
|
|
39370
39971
|
if (verb === "members") {
|
|
39371
39972
|
await runMembers(args);
|
|
39372
39973
|
return;
|