mnemospark 0.1.22 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/README.md.bak +1 -1
- package/dist/cli.js +925 -86
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +51 -0
- package/dist/index.js +933 -91
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/mnemospark/SKILL.md +31 -13
- package/skills/mnemospark/references/commands.md +38 -5
- package/skills/mnemospark/references/state-and-logs.md +40 -0
- package/skills/mnemospark/references/troubleshooting.md +16 -4
package/dist/cli.js
CHANGED
|
@@ -1721,7 +1721,7 @@ async function startProxy(options) {
|
|
|
1721
1721
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1722
1722
|
sendJson(res, 400, {
|
|
1723
1723
|
error: "Bad request",
|
|
1724
|
-
message: "Invalid JSON body for /
|
|
1724
|
+
message: "Invalid JSON body for /mnemospark_cloud price-storage"
|
|
1725
1725
|
});
|
|
1726
1726
|
return;
|
|
1727
1727
|
}
|
|
@@ -1789,7 +1789,7 @@ async function startProxy(options) {
|
|
|
1789
1789
|
});
|
|
1790
1790
|
sendJson(res, 502, {
|
|
1791
1791
|
error: "proxy_error",
|
|
1792
|
-
message: `Failed to forward /
|
|
1792
|
+
message: `Failed to forward /mnemospark_cloud price-storage: ${err instanceof Error ? err.message : String(err)}`
|
|
1793
1793
|
});
|
|
1794
1794
|
}
|
|
1795
1795
|
return;
|
|
@@ -1935,7 +1935,7 @@ async function startProxy(options) {
|
|
|
1935
1935
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1936
1936
|
sendJson(res, 400, {
|
|
1937
1937
|
error: "Bad request",
|
|
1938
|
-
message: "Invalid JSON body for /
|
|
1938
|
+
message: "Invalid JSON body for /mnemospark_cloud upload"
|
|
1939
1939
|
});
|
|
1940
1940
|
return;
|
|
1941
1941
|
}
|
|
@@ -1998,7 +1998,7 @@ async function startProxy(options) {
|
|
|
1998
1998
|
error: "insufficient_balance",
|
|
1999
1999
|
message: `Insufficient USDC balance. Current: ${sufficiency.info.balanceUSD}, Required: ${requiredUSD}`,
|
|
2000
2000
|
wallet: requestPayload.wallet_address,
|
|
2001
|
-
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /
|
|
2001
|
+
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /mnemospark_cloud upload`
|
|
2002
2002
|
});
|
|
2003
2003
|
return;
|
|
2004
2004
|
}
|
|
@@ -2106,7 +2106,7 @@ async function startProxy(options) {
|
|
|
2106
2106
|
});
|
|
2107
2107
|
sendJson(res, 502, {
|
|
2108
2108
|
error: "proxy_error",
|
|
2109
|
-
message: `Failed to forward /
|
|
2109
|
+
message: `Failed to forward /mnemospark_cloud upload: ${err instanceof Error ? err.message : String(err)}`
|
|
2110
2110
|
});
|
|
2111
2111
|
}
|
|
2112
2112
|
return;
|
|
@@ -2124,7 +2124,7 @@ async function startProxy(options) {
|
|
|
2124
2124
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2125
2125
|
sendJson(res, 400, {
|
|
2126
2126
|
error: "Bad request",
|
|
2127
|
-
message: "Invalid JSON body for /
|
|
2127
|
+
message: "Invalid JSON body for /mnemospark_cloud upload/confirm"
|
|
2128
2128
|
});
|
|
2129
2129
|
return;
|
|
2130
2130
|
}
|
|
@@ -2205,7 +2205,7 @@ async function startProxy(options) {
|
|
|
2205
2205
|
});
|
|
2206
2206
|
sendJson(res, 502, {
|
|
2207
2207
|
error: "proxy_error",
|
|
2208
|
-
message: `Failed to forward /
|
|
2208
|
+
message: `Failed to forward /mnemospark_cloud upload/confirm: ${err instanceof Error ? err.message : String(err)}`
|
|
2209
2209
|
});
|
|
2210
2210
|
}
|
|
2211
2211
|
return;
|
|
@@ -2223,7 +2223,7 @@ async function startProxy(options) {
|
|
|
2223
2223
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2224
2224
|
sendJson(res, 400, {
|
|
2225
2225
|
error: "Bad request",
|
|
2226
|
-
message: "Invalid JSON body for /
|
|
2226
|
+
message: "Invalid JSON body for /mnemospark_cloud ls"
|
|
2227
2227
|
});
|
|
2228
2228
|
return;
|
|
2229
2229
|
}
|
|
@@ -2296,7 +2296,7 @@ async function startProxy(options) {
|
|
|
2296
2296
|
});
|
|
2297
2297
|
sendJson(res, 502, {
|
|
2298
2298
|
error: "proxy_error",
|
|
2299
|
-
message: `Failed to forward /
|
|
2299
|
+
message: `Failed to forward /mnemospark_cloud ls: ${err instanceof Error ? err.message : String(err)}`
|
|
2300
2300
|
});
|
|
2301
2301
|
}
|
|
2302
2302
|
return;
|
|
@@ -2316,7 +2316,7 @@ async function startProxy(options) {
|
|
|
2316
2316
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2317
2317
|
sendJson(res, 400, {
|
|
2318
2318
|
error: "Bad request",
|
|
2319
|
-
message: "Invalid JSON body for /
|
|
2319
|
+
message: "Invalid JSON body for /mnemospark_cloud download"
|
|
2320
2320
|
});
|
|
2321
2321
|
return;
|
|
2322
2322
|
}
|
|
@@ -2414,7 +2414,7 @@ async function startProxy(options) {
|
|
|
2414
2414
|
});
|
|
2415
2415
|
sendJson(res, 502, {
|
|
2416
2416
|
error: "proxy_error",
|
|
2417
|
-
message: `Failed to forward /
|
|
2417
|
+
message: `Failed to forward /mnemospark_cloud download: ${err instanceof Error ? err.message : String(err)}`
|
|
2418
2418
|
});
|
|
2419
2419
|
}
|
|
2420
2420
|
return;
|
|
@@ -2432,7 +2432,7 @@ async function startProxy(options) {
|
|
|
2432
2432
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2433
2433
|
sendJson(res, 400, {
|
|
2434
2434
|
error: "Bad request",
|
|
2435
|
-
message: "Invalid JSON body for /
|
|
2435
|
+
message: "Invalid JSON body for /mnemospark_cloud delete"
|
|
2436
2436
|
});
|
|
2437
2437
|
return;
|
|
2438
2438
|
}
|
|
@@ -2509,7 +2509,7 @@ async function startProxy(options) {
|
|
|
2509
2509
|
});
|
|
2510
2510
|
sendJson(res, 502, {
|
|
2511
2511
|
error: "proxy_error",
|
|
2512
|
-
message: `Failed to forward /
|
|
2512
|
+
message: `Failed to forward /mnemospark_cloud delete: ${err instanceof Error ? err.message : String(err)}`
|
|
2513
2513
|
});
|
|
2514
2514
|
}
|
|
2515
2515
|
return;
|
|
@@ -2755,7 +2755,7 @@ import { mkdir as mkdir4 } from "fs/promises";
|
|
|
2755
2755
|
import { homedir as homedir5 } from "os";
|
|
2756
2756
|
import { dirname as dirname4, join as join7 } from "path";
|
|
2757
2757
|
var DB_SUBPATH = join7(".openclaw", "mnemospark", "state.db");
|
|
2758
|
-
var SCHEMA_VERSION =
|
|
2758
|
+
var SCHEMA_VERSION = 3;
|
|
2759
2759
|
function resolveDbPath(homeDir) {
|
|
2760
2760
|
return join7(homeDir ?? homedir5(), DB_SUBPATH);
|
|
2761
2761
|
}
|
|
@@ -2861,6 +2861,21 @@ async function createCloudDatastore(homeDir) {
|
|
|
2861
2861
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_wallet ON friendly_names(wallet_address);
|
|
2862
2862
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_created_at ON friendly_names(created_at);
|
|
2863
2863
|
`);
|
|
2864
|
+
const addOperationsColumn = (columnName, sqlType) => {
|
|
2865
|
+
try {
|
|
2866
|
+
nextDb.exec(`ALTER TABLE operations ADD COLUMN ${columnName} ${sqlType}`);
|
|
2867
|
+
} catch (error) {
|
|
2868
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error);
|
|
2869
|
+
if (!message.includes("duplicate column name")) {
|
|
2870
|
+
throw error;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
};
|
|
2874
|
+
addOperationsColumn("trace_id", "TEXT");
|
|
2875
|
+
addOperationsColumn("orchestrator", "TEXT");
|
|
2876
|
+
addOperationsColumn("subagent_session_id", "TEXT");
|
|
2877
|
+
addOperationsColumn("timeout_seconds", "INTEGER");
|
|
2878
|
+
addOperationsColumn("cancel_requested_at", "TEXT");
|
|
2864
2879
|
nextDb.prepare(
|
|
2865
2880
|
`INSERT INTO schema_migrations(version, applied_at)
|
|
2866
2881
|
VALUES(?, ?)
|
|
@@ -2872,7 +2887,10 @@ async function createCloudDatastore(homeDir) {
|
|
|
2872
2887
|
try {
|
|
2873
2888
|
await ensureReady();
|
|
2874
2889
|
return fn();
|
|
2875
|
-
} catch {
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
2892
|
+
throw error;
|
|
2893
|
+
}
|
|
2876
2894
|
return fallback;
|
|
2877
2895
|
}
|
|
2878
2896
|
};
|
|
@@ -2997,13 +3015,19 @@ async function createCloudDatastore(homeDir) {
|
|
|
2997
3015
|
upsertOperation: async (row) => {
|
|
2998
3016
|
await safe(() => {
|
|
2999
3017
|
const ts = nowIso();
|
|
3018
|
+
const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
3000
3019
|
db.prepare(
|
|
3001
|
-
`INSERT INTO operations(operation_id, type, object_id, quote_id, status, error_code, error_message, started_at, finished_at, created_at, updated_at)
|
|
3002
|
-
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3020
|
+
`INSERT INTO operations(operation_id, type, object_id, quote_id, trace_id, orchestrator, subagent_session_id, timeout_seconds, cancel_requested_at, status, error_code, error_message, started_at, finished_at, created_at, updated_at)
|
|
3021
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3003
3022
|
ON CONFLICT(operation_id) DO UPDATE SET
|
|
3004
3023
|
type=excluded.type,
|
|
3005
3024
|
object_id=COALESCE(excluded.object_id, operations.object_id),
|
|
3006
|
-
quote_id=excluded.quote_id,
|
|
3025
|
+
quote_id=COALESCE(excluded.quote_id, operations.quote_id),
|
|
3026
|
+
trace_id=COALESCE(excluded.trace_id, operations.trace_id),
|
|
3027
|
+
orchestrator=COALESCE(excluded.orchestrator, operations.orchestrator),
|
|
3028
|
+
subagent_session_id=COALESCE(excluded.subagent_session_id, operations.subagent_session_id),
|
|
3029
|
+
timeout_seconds=COALESCE(excluded.timeout_seconds, operations.timeout_seconds),
|
|
3030
|
+
cancel_requested_at=COALESCE(excluded.cancel_requested_at, operations.cancel_requested_at),
|
|
3007
3031
|
status=excluded.status,
|
|
3008
3032
|
error_code=excluded.error_code,
|
|
3009
3033
|
error_message=excluded.error_message,
|
|
@@ -3015,11 +3039,16 @@ async function createCloudDatastore(homeDir) {
|
|
|
3015
3039
|
row.type,
|
|
3016
3040
|
row.object_id,
|
|
3017
3041
|
row.quote_id,
|
|
3042
|
+
row.trace_id ?? null,
|
|
3043
|
+
row.orchestrator ?? null,
|
|
3044
|
+
row.subagent_session_id ?? null,
|
|
3045
|
+
row.timeout_seconds ?? null,
|
|
3046
|
+
row.cancel_requested_at ?? null,
|
|
3018
3047
|
row.status,
|
|
3019
3048
|
row.error_code,
|
|
3020
3049
|
row.error_message,
|
|
3021
3050
|
row.status === "started" ? ts : null,
|
|
3022
|
-
|
|
3051
|
+
terminalStatuses.has(row.status) ? ts : null,
|
|
3023
3052
|
ts,
|
|
3024
3053
|
ts
|
|
3025
3054
|
);
|
|
@@ -3027,7 +3056,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
3027
3056
|
},
|
|
3028
3057
|
findOperationById: async (operationId) => safe(() => {
|
|
3029
3058
|
const row = db.prepare(
|
|
3030
|
-
`SELECT operation_id, type, status, error_code, error_message, started_at, finished_at, updated_at
|
|
3059
|
+
`SELECT operation_id, type, object_id, quote_id, trace_id, orchestrator, subagent_session_id, timeout_seconds, cancel_requested_at, status, error_code, error_message, started_at, finished_at, updated_at
|
|
3031
3060
|
FROM operations
|
|
3032
3061
|
WHERE operation_id = ?
|
|
3033
3062
|
LIMIT 1`
|
|
@@ -3129,7 +3158,9 @@ var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-ha
|
|
|
3129
3158
|
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3130
3159
|
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3131
3160
|
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3161
|
+
var BOOLEAN_OP_STATUS_FLAGS = /* @__PURE__ */ new Set(["cancel"]);
|
|
3132
3162
|
var BOOLEAN_SELECTOR_AND_ASYNC_FLAGS = /* @__PURE__ */ new Set(["latest", "async"]);
|
|
3163
|
+
var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
|
|
3133
3164
|
function expandTilde(path) {
|
|
3134
3165
|
const trimmed = path.trim();
|
|
3135
3166
|
if (trimmed === "~") {
|
|
@@ -3143,29 +3174,54 @@ function expandTilde(path) {
|
|
|
3143
3174
|
var CLOUD_HELP_TEXT = [
|
|
3144
3175
|
"\u2601\uFE0F **mnemospark Cloud Commands**",
|
|
3145
3176
|
"",
|
|
3146
|
-
"\u2022 `/
|
|
3177
|
+
"\u2022 `/mnemospark_cloud` or `/mnemospark_cloud help` \u2014 show this message",
|
|
3147
3178
|
"",
|
|
3148
|
-
"\u2022 `/
|
|
3149
|
-
"
|
|
3179
|
+
"\u2022 `/mnemospark_cloud backup <file|directory> [--name <friendly-name>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3180
|
+
" Purpose: create a local tar+gzip backup object and index it for later upload.",
|
|
3181
|
+
" Required: <file|directory>",
|
|
3150
3182
|
"",
|
|
3151
|
-
"\u2022 `/
|
|
3183
|
+
"\u2022 `/mnemospark_cloud price-storage --wallet-address <addr> --object-id <id> --object-id-hash <hash> --gb <gb> --provider <provider> --region <region>`",
|
|
3184
|
+
" Purpose: request a storage quote before upload.",
|
|
3152
3185
|
" Required: " + REQUIRED_PRICE_STORAGE,
|
|
3153
3186
|
"",
|
|
3154
|
-
"\u2022 `/
|
|
3187
|
+
"\u2022 `/mnemospark_cloud upload --quote-id <quote-id> --wallet-address <addr> --object-id <id> --object-id-hash <hash> [--name <friendly-name>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3188
|
+
" Purpose: upload an encrypted object using a valid quote-id.",
|
|
3155
3189
|
" Required: " + REQUIRED_UPLOAD,
|
|
3156
3190
|
"",
|
|
3157
|
-
"\u2022 `/
|
|
3191
|
+
"\u2022 `/mnemospark_cloud ls --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3192
|
+
" Purpose: look up remote object metadata.",
|
|
3158
3193
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3159
3194
|
"",
|
|
3160
|
-
"\u2022 `/
|
|
3195
|
+
"\u2022 `/mnemospark_cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3196
|
+
" Purpose: fetch an object to local disk.",
|
|
3161
3197
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3162
3198
|
"",
|
|
3163
|
-
"\u2022 `/
|
|
3199
|
+
"\u2022 `/mnemospark_cloud delete --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3200
|
+
" Purpose: remove a remote object and local cron tracking when present.",
|
|
3164
3201
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3165
3202
|
"",
|
|
3166
|
-
"\u2022 `/
|
|
3203
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> [--cancel]`",
|
|
3204
|
+
" Purpose: inspect async operation status, or request cancellation for subagent runs.",
|
|
3167
3205
|
" Required: --operation-id",
|
|
3168
3206
|
"",
|
|
3207
|
+
"Async orchestration flags (`backup`, `upload`, `download` only):",
|
|
3208
|
+
"\u2022 `--async`",
|
|
3209
|
+
" Start operation in background and return quickly with operation-id.",
|
|
3210
|
+
"\u2022 `--orchestrator <inline|subagent>`",
|
|
3211
|
+
" Choose async engine. Default when omitted is `inline`.",
|
|
3212
|
+
" Use `subagent` for explicit subagent session tracking and cancellation.",
|
|
3213
|
+
"\u2022 `--timeout-seconds <n>`",
|
|
3214
|
+
" Optional per-operation timeout. Valid only with `--async --orchestrator subagent`.",
|
|
3215
|
+
" `n` must be a positive integer (seconds).",
|
|
3216
|
+
"\u2022 `op-status --cancel`",
|
|
3217
|
+
" Cancel a subagent-orchestrated operation by operation-id (idempotent).",
|
|
3218
|
+
"",
|
|
3219
|
+
"Examples:",
|
|
3220
|
+
"\u2022 `/mnemospark_cloud upload ... --async --orchestrator subagent`",
|
|
3221
|
+
"\u2022 `/mnemospark_cloud download ... --async --orchestrator subagent --timeout-seconds 900`",
|
|
3222
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id>`",
|
|
3223
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> --cancel`",
|
|
3224
|
+
"",
|
|
3169
3225
|
"Backup creates a tar+gzip object in ~/.openclaw/mnemospark/backup and appends object metadata to ~/.openclaw/mnemospark/object.log. Upload appends storage rows and cron-tracking rows to object.log, and keeps job entries in ~/.openclaw/mnemospark/crontab.txt. All storage commands (price-storage, upload, ls, download, delete) require --wallet-address."
|
|
3170
3226
|
].join("\n");
|
|
3171
3227
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
@@ -3255,9 +3311,73 @@ function parseStorageObjectRequestInput(flags, selector) {
|
|
|
3255
3311
|
location
|
|
3256
3312
|
});
|
|
3257
3313
|
}
|
|
3258
|
-
function
|
|
3314
|
+
function parseOrchestratorMode(value) {
|
|
3315
|
+
if (!value) {
|
|
3316
|
+
return void 0;
|
|
3317
|
+
}
|
|
3318
|
+
const normalized = value.trim().toLowerCase();
|
|
3319
|
+
if (!normalized) {
|
|
3320
|
+
return null;
|
|
3321
|
+
}
|
|
3322
|
+
if (!ORCHESTRATOR_MODES.has(normalized)) {
|
|
3323
|
+
return null;
|
|
3324
|
+
}
|
|
3325
|
+
return normalized;
|
|
3326
|
+
}
|
|
3327
|
+
function parseTimeoutSeconds(value) {
|
|
3328
|
+
if (!value) {
|
|
3329
|
+
return void 0;
|
|
3330
|
+
}
|
|
3331
|
+
const trimmed = value.trim();
|
|
3332
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3335
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
3336
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3337
|
+
return null;
|
|
3338
|
+
}
|
|
3339
|
+
return parsed;
|
|
3340
|
+
}
|
|
3341
|
+
function parseAsyncOperationArgs(flags) {
|
|
3342
|
+
const asyncRequested = flags.async === "true";
|
|
3343
|
+
const hasOrchestratorFlag = typeof flags.orchestrator === "string";
|
|
3344
|
+
const hasTimeoutFlag = typeof flags["timeout-seconds"] === "string";
|
|
3345
|
+
if (!asyncRequested && (hasOrchestratorFlag || hasTimeoutFlag)) {
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
const parsedOrchestrator = parseOrchestratorMode(flags.orchestrator);
|
|
3349
|
+
if (parsedOrchestrator === null) {
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
const parsedTimeoutSeconds = parseTimeoutSeconds(flags["timeout-seconds"]);
|
|
3353
|
+
if (parsedTimeoutSeconds === null) {
|
|
3354
|
+
return null;
|
|
3355
|
+
}
|
|
3356
|
+
if (typeof parsedTimeoutSeconds === "number" && (parsedOrchestrator ?? "inline") !== "subagent") {
|
|
3357
|
+
return null;
|
|
3358
|
+
}
|
|
3359
|
+
return {
|
|
3360
|
+
async: asyncRequested,
|
|
3361
|
+
orchestrator: parsedOrchestrator === void 0 ? void 0 : parsedOrchestrator,
|
|
3362
|
+
timeoutSeconds: parsedTimeoutSeconds === void 0 ? void 0 : parsedTimeoutSeconds
|
|
3363
|
+
};
|
|
3364
|
+
}
|
|
3365
|
+
var INVALID_ASYNC_FLAGS_MESSAGE = "invalid async flags. `--orchestrator`/`--timeout-seconds` require `--async`, and `--timeout-seconds` is only valid with `--orchestrator subagent`.";
|
|
3366
|
+
function stripAsyncControlFlags(args) {
|
|
3259
3367
|
const tokens = tokenizeArgsRaw(args ?? "");
|
|
3260
|
-
const filtered =
|
|
3368
|
+
const filtered = [];
|
|
3369
|
+
for (let idx = 0; idx < tokens.length; idx += 1) {
|
|
3370
|
+
const token = tokens[idx];
|
|
3371
|
+
const lowerToken = token.toLowerCase();
|
|
3372
|
+
if (lowerToken === "--async") {
|
|
3373
|
+
continue;
|
|
3374
|
+
}
|
|
3375
|
+
if (lowerToken === "--orchestrator" || lowerToken === "--timeout-seconds") {
|
|
3376
|
+
idx += 1;
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
filtered.push(token);
|
|
3380
|
+
}
|
|
3261
3381
|
return filtered.join(" ");
|
|
3262
3382
|
}
|
|
3263
3383
|
function parseCloudArgs(args) {
|
|
@@ -3280,8 +3400,21 @@ function parseCloudArgs(args) {
|
|
|
3280
3400
|
if (!backupTarget) {
|
|
3281
3401
|
return { mode: "unknown" };
|
|
3282
3402
|
}
|
|
3283
|
-
const
|
|
3284
|
-
|
|
3403
|
+
const remainingTokens = tokens.slice(1);
|
|
3404
|
+
const flags = remainingTokens.length === 0 ? {} : parseNamedFlagsTokens(remainingTokens, BOOLEAN_ASYNC_FLAGS);
|
|
3405
|
+
if (!flags) {
|
|
3406
|
+
return { mode: "backup-invalid" };
|
|
3407
|
+
}
|
|
3408
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3409
|
+
if (!asyncArgs) {
|
|
3410
|
+
return { mode: "backup-invalid-async" };
|
|
3411
|
+
}
|
|
3412
|
+
return {
|
|
3413
|
+
mode: "backup",
|
|
3414
|
+
backupTarget,
|
|
3415
|
+
friendlyName: flags.name?.trim() || void 0,
|
|
3416
|
+
...asyncArgs
|
|
3417
|
+
};
|
|
3285
3418
|
}
|
|
3286
3419
|
if (subcommand === "price-storage") {
|
|
3287
3420
|
const flags = parseNamedFlags(rest);
|
|
@@ -3307,6 +3440,10 @@ function parseCloudArgs(args) {
|
|
|
3307
3440
|
if (!flags) {
|
|
3308
3441
|
return { mode: "upload-invalid" };
|
|
3309
3442
|
}
|
|
3443
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3444
|
+
if (!asyncArgs) {
|
|
3445
|
+
return { mode: "upload-invalid-async" };
|
|
3446
|
+
}
|
|
3310
3447
|
const quoteId = flags["quote-id"]?.trim();
|
|
3311
3448
|
const walletAddress = flags["wallet-address"]?.trim();
|
|
3312
3449
|
const objectId = flags["object-id"]?.trim();
|
|
@@ -3317,7 +3454,7 @@ function parseCloudArgs(args) {
|
|
|
3317
3454
|
return {
|
|
3318
3455
|
mode: "upload",
|
|
3319
3456
|
friendlyName: flags.name?.trim() || void 0,
|
|
3320
|
-
|
|
3457
|
+
...asyncArgs,
|
|
3321
3458
|
uploadRequest: {
|
|
3322
3459
|
quote_id: quoteId,
|
|
3323
3460
|
wallet_address: walletAddress,
|
|
@@ -3346,6 +3483,10 @@ function parseCloudArgs(args) {
|
|
|
3346
3483
|
if (!flags) {
|
|
3347
3484
|
return { mode: "download-invalid" };
|
|
3348
3485
|
}
|
|
3486
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3487
|
+
if (!asyncArgs) {
|
|
3488
|
+
return { mode: "download-invalid-async" };
|
|
3489
|
+
}
|
|
3349
3490
|
const selector = parseObjectSelector(flags);
|
|
3350
3491
|
if (!selector) {
|
|
3351
3492
|
return { mode: "download-invalid" };
|
|
@@ -3358,7 +3499,7 @@ function parseCloudArgs(args) {
|
|
|
3358
3499
|
mode: "download",
|
|
3359
3500
|
storageObjectRequest: request,
|
|
3360
3501
|
nameSelector: selector.nameSelector,
|
|
3361
|
-
|
|
3502
|
+
...asyncArgs
|
|
3362
3503
|
};
|
|
3363
3504
|
}
|
|
3364
3505
|
if (subcommand === "delete") {
|
|
@@ -3377,12 +3518,12 @@ function parseCloudArgs(args) {
|
|
|
3377
3518
|
return { mode: "delete", storageObjectRequest: request, nameSelector: selector.nameSelector };
|
|
3378
3519
|
}
|
|
3379
3520
|
if (subcommand === "op-status") {
|
|
3380
|
-
const flags = parseNamedFlags(rest);
|
|
3521
|
+
const flags = parseNamedFlags(rest, BOOLEAN_OP_STATUS_FLAGS);
|
|
3381
3522
|
const operationId = flags?.["operation-id"]?.trim();
|
|
3382
3523
|
if (!operationId) {
|
|
3383
3524
|
return { mode: "op-status-invalid" };
|
|
3384
3525
|
}
|
|
3385
|
-
return { mode: "op-status", operationId };
|
|
3526
|
+
return { mode: "op-status", operationId, cancel: flags?.cancel === "true" };
|
|
3386
3527
|
}
|
|
3387
3528
|
return { mode: "unknown" };
|
|
3388
3529
|
}
|
|
@@ -3873,17 +4014,37 @@ async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encrypt
|
|
|
3873
4014
|
if (!headers.has("content-type")) {
|
|
3874
4015
|
headers.set("content-type", "application/octet-stream");
|
|
3875
4016
|
}
|
|
3876
|
-
const
|
|
4017
|
+
const putBody = new Uint8Array(encryptedContent);
|
|
4018
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, {
|
|
3877
4019
|
method: "PUT",
|
|
3878
4020
|
headers,
|
|
3879
|
-
body:
|
|
4021
|
+
body: putBody,
|
|
4022
|
+
redirect: "manual"
|
|
3880
4023
|
});
|
|
3881
|
-
if (
|
|
3882
|
-
|
|
3883
|
-
throw new Error(
|
|
3884
|
-
`Presigned upload failed with status ${response.status}${details ? `: ${details}` : ""}`
|
|
3885
|
-
);
|
|
4024
|
+
if (firstAttempt.ok) {
|
|
4025
|
+
return;
|
|
3886
4026
|
}
|
|
4027
|
+
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4028
|
+
const location = firstAttempt.headers.get("location")?.trim();
|
|
4029
|
+
if (location) {
|
|
4030
|
+
const redirectedAttempt = await fetchImpl(location, {
|
|
4031
|
+
method: "PUT",
|
|
4032
|
+
headers,
|
|
4033
|
+
body: putBody
|
|
4034
|
+
});
|
|
4035
|
+
if (redirectedAttempt.ok) {
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
const redirectedDetails = (await redirectedAttempt.text()).trim();
|
|
4039
|
+
throw new Error(
|
|
4040
|
+
`Presigned upload failed after redirect with status ${redirectedAttempt.status}${redirectedDetails ? `: ${redirectedDetails}` : ""}`
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
const details = (await firstAttempt.text()).trim();
|
|
4045
|
+
throw new Error(
|
|
4046
|
+
`Presigned upload failed with status ${firstAttempt.status}${details ? `: ${details}` : ""}`
|
|
4047
|
+
);
|
|
3887
4048
|
}
|
|
3888
4049
|
async function appendStorageUploadLog(upload, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
|
|
3889
4050
|
return appendObjectLogLine(
|
|
@@ -3954,16 +4115,118 @@ function extractUploadErrorMessage(error) {
|
|
|
3954
4115
|
function formatPriceStorageUserMessage(quote) {
|
|
3955
4116
|
return [
|
|
3956
4117
|
`Your storage quote \`${quote.quote_id}\` is valid for 1 hour, the storage price is \`${quote.storage_price}\` for \`${quote.object_id}\` with file size of \`${quote.object_size_gb}\` in \`${quote.provider}\` \`${quote.location}\``,
|
|
3957
|
-
`If you accept this quote run the command /
|
|
4118
|
+
`If you accept this quote run the command /mnemospark_cloud upload --quote-id \`${quote.quote_id}\` --wallet-address \`${quote.addr}\` --object-id \`${quote.object_id}\` --object-id-hash \`${quote.object_id_hash}\``
|
|
3958
4119
|
].join("\n");
|
|
3959
4120
|
}
|
|
3960
4121
|
function formatStorageLsUserMessage(result, requestedObjectKey) {
|
|
3961
4122
|
const objectId = result.object_id ?? result.key;
|
|
3962
4123
|
return `${objectId} with ${requestedObjectKey} is ${result.size_bytes} in ${result.bucket}`;
|
|
3963
4124
|
}
|
|
4125
|
+
function createInProcessSubagentOrchestrator() {
|
|
4126
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
4127
|
+
const completeSession = async (sessionId, handler) => {
|
|
4128
|
+
const session = sessions.get(sessionId);
|
|
4129
|
+
if (!session || session.terminal) {
|
|
4130
|
+
return false;
|
|
4131
|
+
}
|
|
4132
|
+
session.terminal = true;
|
|
4133
|
+
if (session.timeoutHandle) {
|
|
4134
|
+
clearTimeout(session.timeoutHandle);
|
|
4135
|
+
}
|
|
4136
|
+
sessions.delete(sessionId);
|
|
4137
|
+
await handler(session.hooks);
|
|
4138
|
+
return true;
|
|
4139
|
+
};
|
|
4140
|
+
return {
|
|
4141
|
+
dispatch: async (input) => {
|
|
4142
|
+
const sessionId = `agent:mnemospark:subagent:${randomUUID3()}`;
|
|
4143
|
+
const state = {
|
|
4144
|
+
terminal: false,
|
|
4145
|
+
cancelRequested: false,
|
|
4146
|
+
hooks: input.hooks
|
|
4147
|
+
};
|
|
4148
|
+
sessions.set(sessionId, state);
|
|
4149
|
+
if (typeof input.timeoutSeconds === "number" && input.timeoutSeconds > 0) {
|
|
4150
|
+
state.timeoutHandle = setTimeout(() => {
|
|
4151
|
+
void completeSession(sessionId, async (hooks) => {
|
|
4152
|
+
await hooks?.onTimedOut?.(sessionId);
|
|
4153
|
+
});
|
|
4154
|
+
}, input.timeoutSeconds * 1e3);
|
|
4155
|
+
}
|
|
4156
|
+
setTimeout(() => {
|
|
4157
|
+
void (async () => {
|
|
4158
|
+
try {
|
|
4159
|
+
await input.hooks?.onRunning?.(sessionId);
|
|
4160
|
+
await input.hooks?.onProgress?.(sessionId, "subagent execution started");
|
|
4161
|
+
const result = await input.runTask();
|
|
4162
|
+
const session = sessions.get(sessionId);
|
|
4163
|
+
if (!session || session.terminal) {
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
if (session.cancelRequested) {
|
|
4167
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4168
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4169
|
+
});
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
if (result.isError) {
|
|
4173
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4174
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4175
|
+
code: "ASYNC_FAILED",
|
|
4176
|
+
message: result.text
|
|
4177
|
+
});
|
|
4178
|
+
});
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4182
|
+
await hooks?.onCompleted?.(sessionId, result);
|
|
4183
|
+
});
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4186
|
+
const session = sessions.get(sessionId);
|
|
4187
|
+
if (!session || session.terminal) {
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
4190
|
+
if (session.cancelRequested) {
|
|
4191
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4192
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4193
|
+
});
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4196
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4197
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4198
|
+
code: "ASYNC_EXCEPTION",
|
|
4199
|
+
message
|
|
4200
|
+
});
|
|
4201
|
+
});
|
|
4202
|
+
}
|
|
4203
|
+
})();
|
|
4204
|
+
}, 0);
|
|
4205
|
+
return { sessionId };
|
|
4206
|
+
},
|
|
4207
|
+
cancel: async (sessionId, reason) => {
|
|
4208
|
+
const session = sessions.get(sessionId);
|
|
4209
|
+
if (!session) {
|
|
4210
|
+
return { accepted: false };
|
|
4211
|
+
}
|
|
4212
|
+
if (session.terminal) {
|
|
4213
|
+
return { accepted: false, alreadyTerminal: true };
|
|
4214
|
+
}
|
|
4215
|
+
session.cancelRequested = true;
|
|
4216
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4217
|
+
await hooks?.onCancelled?.(sessionId, reason ?? "cancel requested");
|
|
4218
|
+
});
|
|
4219
|
+
return { accepted: true };
|
|
4220
|
+
}
|
|
4221
|
+
};
|
|
4222
|
+
}
|
|
3964
4223
|
function createCloudCommand(options = {}) {
|
|
4224
|
+
const subagentOrchestrator = options.subagentOrchestrator ?? createInProcessSubagentOrchestrator();
|
|
3965
4225
|
return {
|
|
3966
|
-
name: "
|
|
4226
|
+
name: "mnemospark_cloud",
|
|
4227
|
+
nativeNames: {
|
|
4228
|
+
default: "mnemospark_cloud"
|
|
4229
|
+
},
|
|
3967
4230
|
description: "Manage mnemospark cloud storage workflow commands",
|
|
3968
4231
|
acceptsArgs: true,
|
|
3969
4232
|
requireAuth: true,
|
|
@@ -3987,6 +4250,7 @@ function createCloudCommand(options = {}) {
|
|
|
3987
4250
|
proxyQuoteOptions: options.proxyQuoteOptions,
|
|
3988
4251
|
proxyUploadOptions: options.proxyUploadOptions,
|
|
3989
4252
|
proxyUploadConfirmOptions: options.proxyUploadConfirmOptions,
|
|
4253
|
+
subagentOrchestrator,
|
|
3990
4254
|
proxyStorageOptions: options.proxyStorageOptions
|
|
3991
4255
|
});
|
|
3992
4256
|
} catch (outerError) {
|
|
@@ -3996,7 +4260,42 @@ function createCloudCommand(options = {}) {
|
|
|
3996
4260
|
}
|
|
3997
4261
|
};
|
|
3998
4262
|
}
|
|
3999
|
-
async function
|
|
4263
|
+
async function resolveFriendlyNameFromManifest(params, homeDir) {
|
|
4264
|
+
const manifestPath = join8(homeDir ?? homedir6(), ".openclaw", "mnemospark", "manifest.jsonl");
|
|
4265
|
+
let manifestRaw;
|
|
4266
|
+
try {
|
|
4267
|
+
manifestRaw = await readFile3(manifestPath, "utf-8");
|
|
4268
|
+
} catch {
|
|
4269
|
+
return { objectKey: null, matchCount: 0 };
|
|
4270
|
+
}
|
|
4271
|
+
const wallet = params.walletAddress.trim();
|
|
4272
|
+
const name = params.friendlyName.trim();
|
|
4273
|
+
const atMs = params.at ? Date.parse(params.at) : Number.NaN;
|
|
4274
|
+
const hasAt = Number.isFinite(atMs);
|
|
4275
|
+
const rows = manifestRaw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
4276
|
+
try {
|
|
4277
|
+
return JSON.parse(line);
|
|
4278
|
+
} catch {
|
|
4279
|
+
return null;
|
|
4280
|
+
}
|
|
4281
|
+
}).filter((row) => Boolean(row)).filter((row) => {
|
|
4282
|
+
if (!row.object_key || !row.friendly_name || !row.wallet_address || !row.created_at)
|
|
4283
|
+
return false;
|
|
4284
|
+
if (row.friendly_name !== name) return false;
|
|
4285
|
+
if (row.wallet_address.trim() !== wallet) return false;
|
|
4286
|
+
if (params.latest || !hasAt) return true;
|
|
4287
|
+
const createdAtMs = Date.parse(row.created_at);
|
|
4288
|
+
return Number.isFinite(createdAtMs) && createdAtMs <= atMs;
|
|
4289
|
+
}).sort((a, b) => Date.parse(b.created_at ?? "") - Date.parse(a.created_at ?? ""));
|
|
4290
|
+
if (rows.length === 0) {
|
|
4291
|
+
return { objectKey: null, matchCount: 0 };
|
|
4292
|
+
}
|
|
4293
|
+
if (!params.latest && !hasAt && rows.length > 1) {
|
|
4294
|
+
return { objectKey: null, matchCount: rows.length };
|
|
4295
|
+
}
|
|
4296
|
+
return { objectKey: rows[0].object_key ?? null, matchCount: rows.length };
|
|
4297
|
+
}
|
|
4298
|
+
async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir) {
|
|
4000
4299
|
if (!selector) {
|
|
4001
4300
|
const parsedRequest2 = parseStorageObjectRequest(request);
|
|
4002
4301
|
if (!parsedRequest2) {
|
|
@@ -4004,6 +4303,12 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
4004
4303
|
}
|
|
4005
4304
|
return { request: parsedRequest2 };
|
|
4006
4305
|
}
|
|
4306
|
+
let sqliteUnavailable = false;
|
|
4307
|
+
try {
|
|
4308
|
+
await datastore.ensureReady();
|
|
4309
|
+
} catch {
|
|
4310
|
+
sqliteUnavailable = true;
|
|
4311
|
+
}
|
|
4007
4312
|
const matches = await datastore.countFriendlyNameMatches(request.wallet_address, selector.name);
|
|
4008
4313
|
if (matches > 1 && !selector.latest && !selector.at) {
|
|
4009
4314
|
return {
|
|
@@ -4016,18 +4321,41 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
4016
4321
|
latest: selector.latest,
|
|
4017
4322
|
at: selector.at
|
|
4018
4323
|
});
|
|
4019
|
-
|
|
4324
|
+
let resolvedObjectKey = resolved?.objectKey ?? null;
|
|
4325
|
+
let degradedWarning;
|
|
4326
|
+
if (!resolvedObjectKey && sqliteUnavailable) {
|
|
4327
|
+
const manifestResolved = await resolveFriendlyNameFromManifest(
|
|
4328
|
+
{
|
|
4329
|
+
walletAddress: request.wallet_address,
|
|
4330
|
+
friendlyName: selector.name,
|
|
4331
|
+
latest: selector.latest,
|
|
4332
|
+
at: selector.at
|
|
4333
|
+
},
|
|
4334
|
+
homeDir
|
|
4335
|
+
);
|
|
4336
|
+
if (manifestResolved.matchCount > 1 && !selector.latest && !selector.at) {
|
|
4337
|
+
return {
|
|
4338
|
+
error: `Multiple objects match --name ${selector.name}. Add --latest or --at <timestamp>.`
|
|
4339
|
+
};
|
|
4340
|
+
}
|
|
4341
|
+
resolvedObjectKey = manifestResolved.objectKey;
|
|
4342
|
+
if (resolvedObjectKey) {
|
|
4343
|
+
degradedWarning = "SQLite friendly-name index unavailable; resolved --name via manifest.jsonl fallback.";
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
if (!resolvedObjectKey) {
|
|
4020
4347
|
return { error: `No object found for --name ${selector.name}.` };
|
|
4021
4348
|
}
|
|
4022
4349
|
const parsedRequest = parseStorageObjectRequest({
|
|
4023
4350
|
...request,
|
|
4024
|
-
object_key:
|
|
4351
|
+
object_key: resolvedObjectKey
|
|
4025
4352
|
});
|
|
4026
4353
|
if (!parsedRequest) {
|
|
4027
4354
|
return { error: "Cannot resolve storage object request." };
|
|
4028
4355
|
}
|
|
4029
4356
|
return {
|
|
4030
|
-
request: parsedRequest
|
|
4357
|
+
request: parsedRequest,
|
|
4358
|
+
degradedWarning
|
|
4031
4359
|
};
|
|
4032
4360
|
}
|
|
4033
4361
|
async function emitCloudEvent(eventType, details, homeDir) {
|
|
@@ -4047,6 +4375,38 @@ async function emitCloudEventBestEffort(eventType, details, homeDir) {
|
|
|
4047
4375
|
} catch {
|
|
4048
4376
|
}
|
|
4049
4377
|
}
|
|
4378
|
+
function toOperationEventPayload(eventType, context) {
|
|
4379
|
+
return {
|
|
4380
|
+
operation_id: context.operationId,
|
|
4381
|
+
trace_id: context.traceId,
|
|
4382
|
+
event_type: eventType,
|
|
4383
|
+
status: context.status,
|
|
4384
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4385
|
+
wallet_address: context.walletAddress ?? void 0,
|
|
4386
|
+
object_id: context.objectId ?? void 0,
|
|
4387
|
+
object_key: context.objectKey ?? void 0,
|
|
4388
|
+
quote_id: context.quoteId ?? void 0,
|
|
4389
|
+
orchestrator: context.orchestrator ?? void 0,
|
|
4390
|
+
"subagent-session-id": context.subagentSessionId ?? void 0,
|
|
4391
|
+
"timeout-seconds": context.timeoutSeconds ?? void 0,
|
|
4392
|
+
"error-code": context.errorCode ?? void 0,
|
|
4393
|
+
"error-message": context.errorMessage ?? void 0,
|
|
4394
|
+
progress: context.progressMessage ?? void 0
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
async function emitOperationEvent(eventType, context, homeDir) {
|
|
4398
|
+
const payload = toOperationEventPayload(eventType, context);
|
|
4399
|
+
await Promise.all([
|
|
4400
|
+
appendJsonlEvent("events.jsonl", payload, homeDir),
|
|
4401
|
+
appendJsonlEvent("proxy-events.jsonl", payload, homeDir)
|
|
4402
|
+
]);
|
|
4403
|
+
}
|
|
4404
|
+
async function emitOperationEventBestEffort(eventType, context, homeDir) {
|
|
4405
|
+
try {
|
|
4406
|
+
await emitOperationEvent(eventType, context, homeDir);
|
|
4407
|
+
} catch {
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4050
4410
|
function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
4051
4411
|
const operationId = forcedOperationId?.trim() || randomUUID3();
|
|
4052
4412
|
const traceId = forcedTraceId?.trim() || randomUUID3();
|
|
@@ -4067,6 +4427,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4067
4427
|
const requestStorageLs = options.requestStorageLsFn;
|
|
4068
4428
|
const requestStorageDownload = options.requestStorageDownloadFn;
|
|
4069
4429
|
const requestStorageDelete = options.requestStorageDeleteFn;
|
|
4430
|
+
const subagentOrchestrator = options.subagentOrchestrator;
|
|
4070
4431
|
if (parsed.mode === "help" || parsed.mode === "unknown") {
|
|
4071
4432
|
return {
|
|
4072
4433
|
text: CLOUD_HELP_TEXT,
|
|
@@ -4079,12 +4440,30 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4079
4440
|
isError: true
|
|
4080
4441
|
};
|
|
4081
4442
|
}
|
|
4443
|
+
if (parsed.mode === "backup-invalid") {
|
|
4444
|
+
return {
|
|
4445
|
+
text: "Cannot build storage object",
|
|
4446
|
+
isError: true
|
|
4447
|
+
};
|
|
4448
|
+
}
|
|
4449
|
+
if (parsed.mode === "backup-invalid-async") {
|
|
4450
|
+
return {
|
|
4451
|
+
text: `Cannot build storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4452
|
+
isError: true
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4082
4455
|
if (parsed.mode === "upload-invalid") {
|
|
4083
4456
|
return {
|
|
4084
4457
|
text: `Cannot upload storage object: required arguments are ${REQUIRED_UPLOAD}.`,
|
|
4085
4458
|
isError: true
|
|
4086
4459
|
};
|
|
4087
4460
|
}
|
|
4461
|
+
if (parsed.mode === "upload-invalid-async") {
|
|
4462
|
+
return {
|
|
4463
|
+
text: `Cannot upload storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4464
|
+
isError: true
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4088
4467
|
if (parsed.mode === "ls-invalid") {
|
|
4089
4468
|
return {
|
|
4090
4469
|
text: `Cannot list storage object: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4097,6 +4476,12 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4097
4476
|
isError: true
|
|
4098
4477
|
};
|
|
4099
4478
|
}
|
|
4479
|
+
if (parsed.mode === "download-invalid-async") {
|
|
4480
|
+
return {
|
|
4481
|
+
text: `Cannot download file: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4482
|
+
isError: true
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4100
4485
|
if (parsed.mode === "delete-invalid") {
|
|
4101
4486
|
return {
|
|
4102
4487
|
text: `Cannot delete file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4110,70 +4495,450 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4110
4495
|
};
|
|
4111
4496
|
}
|
|
4112
4497
|
const datastore = await createCloudDatastore(objectLogHomeDir);
|
|
4498
|
+
const terminalOperationStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
4499
|
+
const isTerminalOperationStatus = (status) => terminalOperationStatuses.has(status);
|
|
4500
|
+
const formatOperationStatus = (operation) => ({
|
|
4501
|
+
text: [
|
|
4502
|
+
`operation-id: ${operation.operation_id}`,
|
|
4503
|
+
`type: ${operation.type}`,
|
|
4504
|
+
`status: ${operation.status}`,
|
|
4505
|
+
`started-at: ${operation.started_at ?? "n/a"}`,
|
|
4506
|
+
`finished-at: ${operation.finished_at ?? "n/a"}`,
|
|
4507
|
+
operation.orchestrator ? `orchestrator: ${operation.orchestrator}` : null,
|
|
4508
|
+
operation.subagent_session_id ? `subagent-session-id: ${operation.subagent_session_id}` : null,
|
|
4509
|
+
operation.timeout_seconds ? `timeout-seconds: ${operation.timeout_seconds}` : null,
|
|
4510
|
+
operation.error_code ? `error-code: ${operation.error_code}` : null,
|
|
4511
|
+
operation.error_message ? `error-message: ${operation.error_message}` : null
|
|
4512
|
+
].filter((v) => Boolean(v)).join("\n"),
|
|
4513
|
+
isError: operation.status === "failed" || operation.status === "cancelled" || operation.status === "timed_out"
|
|
4514
|
+
});
|
|
4113
4515
|
if (parsed.mode === "op-status") {
|
|
4114
|
-
|
|
4516
|
+
let operation = await datastore.findOperationById(parsed.operationId);
|
|
4115
4517
|
if (!operation) {
|
|
4116
4518
|
return {
|
|
4117
4519
|
text: `Operation not found: ${parsed.operationId}`,
|
|
4118
4520
|
isError: true
|
|
4119
4521
|
};
|
|
4120
4522
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4523
|
+
if (parsed.cancel) {
|
|
4524
|
+
if (operation.orchestrator !== "subagent" || !operation.subagent_session_id) {
|
|
4525
|
+
return {
|
|
4526
|
+
text: "Cancellation is only supported for subagent-orchestrated operations.",
|
|
4527
|
+
isError: true
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
if (!isTerminalOperationStatus(operation.status)) {
|
|
4531
|
+
const traceId = operation.trace_id ?? randomUUID3();
|
|
4532
|
+
const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4533
|
+
await datastore.upsertOperation({
|
|
4534
|
+
operation_id: operation.operation_id,
|
|
4535
|
+
type: operation.type,
|
|
4536
|
+
object_id: operation.object_id,
|
|
4537
|
+
quote_id: operation.quote_id,
|
|
4538
|
+
trace_id: traceId,
|
|
4539
|
+
orchestrator: "subagent",
|
|
4540
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4541
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4542
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4543
|
+
status: "running",
|
|
4544
|
+
error_code: null,
|
|
4545
|
+
error_message: null
|
|
4546
|
+
});
|
|
4547
|
+
await emitOperationEventBestEffort(
|
|
4548
|
+
"operation.cancel.requested",
|
|
4549
|
+
{
|
|
4550
|
+
operationId: operation.operation_id,
|
|
4551
|
+
traceId,
|
|
4552
|
+
status: "running",
|
|
4553
|
+
objectId: operation.object_id,
|
|
4554
|
+
quoteId: operation.quote_id,
|
|
4555
|
+
orchestrator: "subagent",
|
|
4556
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4557
|
+
timeoutSeconds: operation.timeout_seconds
|
|
4558
|
+
},
|
|
4559
|
+
objectLogHomeDir
|
|
4560
|
+
);
|
|
4561
|
+
const cancelResult = await subagentOrchestrator.cancel(
|
|
4562
|
+
operation.subagent_session_id,
|
|
4563
|
+
"cancel requested by op-status"
|
|
4564
|
+
);
|
|
4565
|
+
if (cancelResult.accepted || cancelResult.alreadyTerminal) {
|
|
4566
|
+
const afterCancel = await datastore.findOperationById(parsed.operationId);
|
|
4567
|
+
if (afterCancel && !isTerminalOperationStatus(afterCancel.status)) {
|
|
4568
|
+
await datastore.upsertOperation({
|
|
4569
|
+
operation_id: operation.operation_id,
|
|
4570
|
+
type: operation.type,
|
|
4571
|
+
object_id: operation.object_id,
|
|
4572
|
+
quote_id: operation.quote_id,
|
|
4573
|
+
trace_id: traceId,
|
|
4574
|
+
orchestrator: "subagent",
|
|
4575
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4576
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4577
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4578
|
+
status: "cancelled",
|
|
4579
|
+
error_code: "ASYNC_CANCELLED",
|
|
4580
|
+
error_message: "Operation cancelled by user request."
|
|
4581
|
+
});
|
|
4582
|
+
await emitOperationEventBestEffort(
|
|
4583
|
+
"operation.cancelled",
|
|
4584
|
+
{
|
|
4585
|
+
operationId: operation.operation_id,
|
|
4586
|
+
traceId,
|
|
4587
|
+
status: "cancelled",
|
|
4588
|
+
objectId: operation.object_id,
|
|
4589
|
+
quoteId: operation.quote_id,
|
|
4590
|
+
orchestrator: "subagent",
|
|
4591
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4592
|
+
timeoutSeconds: operation.timeout_seconds,
|
|
4593
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4594
|
+
errorMessage: "Operation cancelled by user request."
|
|
4595
|
+
},
|
|
4596
|
+
objectLogHomeDir
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
}
|
|
4601
|
+
operation = await datastore.findOperationById(parsed.operationId);
|
|
4602
|
+
if (!operation) {
|
|
4603
|
+
return {
|
|
4604
|
+
text: `Operation not found: ${parsed.operationId}`,
|
|
4605
|
+
isError: true
|
|
4606
|
+
};
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
return formatOperationStatus(operation);
|
|
4133
4610
|
}
|
|
4134
|
-
if ((parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4611
|
+
if ((parsed.mode === "backup" || parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4135
4612
|
const asyncCorrelation = buildRequestCorrelation();
|
|
4136
4613
|
const operationId = asyncCorrelation.operationId;
|
|
4137
4614
|
const opType = parsed.mode;
|
|
4138
4615
|
const opObject = parsed.mode === "upload" ? parsed.uploadRequest.object_id : null;
|
|
4139
4616
|
const opQuote = parsed.mode === "upload" ? parsed.uploadRequest.quote_id : null;
|
|
4617
|
+
const orchestratorMode = parsed.orchestrator ?? "inline";
|
|
4618
|
+
const timeoutSeconds = orchestratorMode === "subagent" ? parsed.timeoutSeconds ?? null : null;
|
|
4619
|
+
const eventContextBase = {
|
|
4620
|
+
operationId,
|
|
4621
|
+
traceId: asyncCorrelation.traceId,
|
|
4622
|
+
walletAddress: parsed.mode === "upload" ? parsed.uploadRequest.wallet_address : parsed.mode === "download" ? parsed.storageObjectRequest.wallet_address : null,
|
|
4623
|
+
objectId: opObject,
|
|
4624
|
+
objectKey: parsed.mode === "download" ? parsed.storageObjectRequest.object_key ?? null : null,
|
|
4625
|
+
quoteId: opQuote,
|
|
4626
|
+
orchestrator: orchestratorMode,
|
|
4627
|
+
timeoutSeconds
|
|
4628
|
+
};
|
|
4140
4629
|
await datastore.upsertOperation({
|
|
4141
4630
|
operation_id: operationId,
|
|
4142
4631
|
type: opType,
|
|
4143
4632
|
object_id: opObject,
|
|
4144
4633
|
quote_id: opQuote,
|
|
4634
|
+
trace_id: asyncCorrelation.traceId,
|
|
4635
|
+
orchestrator: orchestratorMode,
|
|
4636
|
+
timeout_seconds: timeoutSeconds,
|
|
4145
4637
|
status: "started",
|
|
4146
4638
|
error_code: null,
|
|
4147
4639
|
error_message: null
|
|
4148
4640
|
});
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4641
|
+
await emitOperationEventBestEffort(
|
|
4642
|
+
"operation.dispatched",
|
|
4643
|
+
{ ...eventContextBase, status: "started" },
|
|
4644
|
+
objectLogHomeDir
|
|
4645
|
+
);
|
|
4646
|
+
const syncArgs = stripAsyncControlFlags(ctx.args);
|
|
4647
|
+
if (orchestratorMode === "subagent") {
|
|
4648
|
+
const subagentTask = {
|
|
4649
|
+
schema: "mnemospark.subagent-task.v1",
|
|
4650
|
+
operationId,
|
|
4651
|
+
traceId: asyncCorrelation.traceId,
|
|
4652
|
+
command: parsed.mode,
|
|
4653
|
+
args: syncArgs,
|
|
4654
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4655
|
+
requestedBy: {
|
|
4656
|
+
pluginCommand: "mnemospark_cloud",
|
|
4657
|
+
chatId: ctx.channel,
|
|
4658
|
+
senderId: ctx.senderId
|
|
4659
|
+
}
|
|
4660
|
+
};
|
|
4661
|
+
try {
|
|
4662
|
+
const dispatchResult = await subagentOrchestrator.dispatch({
|
|
4663
|
+
task: subagentTask,
|
|
4664
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4665
|
+
runTask: async () => runCloudCommandHandler(
|
|
4666
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4667
|
+
options,
|
|
4668
|
+
{
|
|
4669
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4670
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4671
|
+
}
|
|
4672
|
+
),
|
|
4673
|
+
hooks: {
|
|
4674
|
+
onRunning: async (sessionId) => {
|
|
4675
|
+
await datastore.upsertOperation({
|
|
4676
|
+
operation_id: operationId,
|
|
4677
|
+
type: opType,
|
|
4678
|
+
object_id: opObject,
|
|
4679
|
+
quote_id: opQuote,
|
|
4680
|
+
trace_id: asyncCorrelation.traceId,
|
|
4681
|
+
orchestrator: "subagent",
|
|
4682
|
+
subagent_session_id: sessionId,
|
|
4683
|
+
timeout_seconds: timeoutSeconds,
|
|
4684
|
+
status: "running",
|
|
4685
|
+
error_code: null,
|
|
4686
|
+
error_message: null
|
|
4687
|
+
});
|
|
4688
|
+
await emitOperationEventBestEffort(
|
|
4689
|
+
"operation.progress",
|
|
4690
|
+
{
|
|
4691
|
+
...eventContextBase,
|
|
4692
|
+
status: "running",
|
|
4693
|
+
subagentSessionId: sessionId,
|
|
4694
|
+
progressMessage: "subagent running"
|
|
4695
|
+
},
|
|
4696
|
+
objectLogHomeDir
|
|
4697
|
+
);
|
|
4698
|
+
},
|
|
4699
|
+
onProgress: async (sessionId, message) => {
|
|
4700
|
+
await emitOperationEventBestEffort(
|
|
4701
|
+
"operation.progress",
|
|
4702
|
+
{
|
|
4703
|
+
...eventContextBase,
|
|
4704
|
+
status: "running",
|
|
4705
|
+
subagentSessionId: sessionId,
|
|
4706
|
+
progressMessage: message
|
|
4707
|
+
},
|
|
4708
|
+
objectLogHomeDir
|
|
4709
|
+
);
|
|
4710
|
+
},
|
|
4711
|
+
onCompleted: async (sessionId) => {
|
|
4712
|
+
await datastore.upsertOperation({
|
|
4713
|
+
operation_id: operationId,
|
|
4714
|
+
type: opType,
|
|
4715
|
+
object_id: opObject,
|
|
4716
|
+
quote_id: opQuote,
|
|
4717
|
+
trace_id: asyncCorrelation.traceId,
|
|
4718
|
+
orchestrator: "subagent",
|
|
4719
|
+
subagent_session_id: sessionId,
|
|
4720
|
+
timeout_seconds: timeoutSeconds,
|
|
4721
|
+
status: "succeeded",
|
|
4722
|
+
error_code: null,
|
|
4723
|
+
error_message: null
|
|
4724
|
+
});
|
|
4725
|
+
await emitOperationEventBestEffort(
|
|
4726
|
+
"operation.completed",
|
|
4727
|
+
{
|
|
4728
|
+
...eventContextBase,
|
|
4729
|
+
status: "succeeded",
|
|
4730
|
+
subagentSessionId: sessionId
|
|
4731
|
+
},
|
|
4732
|
+
objectLogHomeDir
|
|
4733
|
+
);
|
|
4734
|
+
},
|
|
4735
|
+
onFailed: async (sessionId, details) => {
|
|
4736
|
+
await datastore.upsertOperation({
|
|
4737
|
+
operation_id: operationId,
|
|
4738
|
+
type: opType,
|
|
4739
|
+
object_id: opObject,
|
|
4740
|
+
quote_id: opQuote,
|
|
4741
|
+
trace_id: asyncCorrelation.traceId,
|
|
4742
|
+
orchestrator: "subagent",
|
|
4743
|
+
subagent_session_id: sessionId,
|
|
4744
|
+
timeout_seconds: timeoutSeconds,
|
|
4745
|
+
status: "failed",
|
|
4746
|
+
error_code: details.code,
|
|
4747
|
+
error_message: details.message
|
|
4748
|
+
});
|
|
4749
|
+
await emitOperationEventBestEffort(
|
|
4750
|
+
"operation.completed",
|
|
4751
|
+
{
|
|
4752
|
+
...eventContextBase,
|
|
4753
|
+
status: "failed",
|
|
4754
|
+
subagentSessionId: sessionId,
|
|
4755
|
+
errorCode: details.code,
|
|
4756
|
+
errorMessage: details.message
|
|
4757
|
+
},
|
|
4758
|
+
objectLogHomeDir
|
|
4759
|
+
);
|
|
4760
|
+
},
|
|
4761
|
+
onCancelled: async (sessionId, reason) => {
|
|
4762
|
+
await datastore.upsertOperation({
|
|
4763
|
+
operation_id: operationId,
|
|
4764
|
+
type: opType,
|
|
4765
|
+
object_id: opObject,
|
|
4766
|
+
quote_id: opQuote,
|
|
4767
|
+
trace_id: asyncCorrelation.traceId,
|
|
4768
|
+
orchestrator: "subagent",
|
|
4769
|
+
subagent_session_id: sessionId,
|
|
4770
|
+
timeout_seconds: timeoutSeconds,
|
|
4771
|
+
cancel_requested_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4772
|
+
status: "cancelled",
|
|
4773
|
+
error_code: "ASYNC_CANCELLED",
|
|
4774
|
+
error_message: reason ?? "Operation cancelled."
|
|
4775
|
+
});
|
|
4776
|
+
await emitOperationEventBestEffort(
|
|
4777
|
+
"operation.cancelled",
|
|
4778
|
+
{
|
|
4779
|
+
...eventContextBase,
|
|
4780
|
+
status: "cancelled",
|
|
4781
|
+
subagentSessionId: sessionId,
|
|
4782
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4783
|
+
errorMessage: reason ?? "Operation cancelled."
|
|
4784
|
+
},
|
|
4785
|
+
objectLogHomeDir
|
|
4786
|
+
);
|
|
4787
|
+
},
|
|
4788
|
+
onTimedOut: async (sessionId) => {
|
|
4789
|
+
await datastore.upsertOperation({
|
|
4790
|
+
operation_id: operationId,
|
|
4791
|
+
type: opType,
|
|
4792
|
+
object_id: opObject,
|
|
4793
|
+
quote_id: opQuote,
|
|
4794
|
+
trace_id: asyncCorrelation.traceId,
|
|
4795
|
+
orchestrator: "subagent",
|
|
4796
|
+
subagent_session_id: sessionId,
|
|
4797
|
+
timeout_seconds: timeoutSeconds,
|
|
4798
|
+
status: "timed_out",
|
|
4799
|
+
error_code: "ASYNC_TIMEOUT",
|
|
4800
|
+
error_message: "Operation timed out."
|
|
4801
|
+
});
|
|
4802
|
+
await emitOperationEventBestEffort(
|
|
4803
|
+
"operation.timed_out",
|
|
4804
|
+
{
|
|
4805
|
+
...eventContextBase,
|
|
4806
|
+
status: "timed_out",
|
|
4807
|
+
subagentSessionId: sessionId,
|
|
4808
|
+
errorCode: "ASYNC_TIMEOUT",
|
|
4809
|
+
errorMessage: "Operation timed out."
|
|
4810
|
+
},
|
|
4811
|
+
objectLogHomeDir
|
|
4812
|
+
);
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
});
|
|
4816
|
+
const operationAfterDispatch = await datastore.findOperationById(operationId);
|
|
4817
|
+
if (operationAfterDispatch?.subagent_session_id !== dispatchResult.sessionId) {
|
|
4818
|
+
await datastore.upsertOperation({
|
|
4819
|
+
operation_id: operationId,
|
|
4820
|
+
type: opType,
|
|
4821
|
+
object_id: opObject,
|
|
4822
|
+
quote_id: opQuote,
|
|
4823
|
+
trace_id: asyncCorrelation.traceId,
|
|
4824
|
+
orchestrator: "subagent",
|
|
4825
|
+
subagent_session_id: dispatchResult.sessionId,
|
|
4826
|
+
timeout_seconds: timeoutSeconds,
|
|
4827
|
+
status: operationAfterDispatch?.status ?? "started",
|
|
4828
|
+
error_code: operationAfterDispatch?.error_code ?? null,
|
|
4829
|
+
error_message: operationAfterDispatch?.error_message ?? null
|
|
4830
|
+
});
|
|
4831
|
+
}
|
|
4832
|
+
return {
|
|
4833
|
+
text: [
|
|
4834
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4835
|
+
`orchestrator: subagent`,
|
|
4836
|
+
`subagent-session-id: ${dispatchResult.sessionId}`,
|
|
4837
|
+
timeoutSeconds ? `timeout-seconds: ${timeoutSeconds}` : null,
|
|
4838
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4839
|
+
].filter((line) => Boolean(line)).join("\n")
|
|
4840
|
+
};
|
|
4841
|
+
} catch (dispatchError) {
|
|
4842
|
+
const dispatchMessage = dispatchError instanceof Error ? dispatchError.message : String(dispatchError);
|
|
4843
|
+
await datastore.upsertOperation({
|
|
4844
|
+
operation_id: operationId,
|
|
4845
|
+
type: opType,
|
|
4846
|
+
object_id: opObject,
|
|
4847
|
+
quote_id: opQuote,
|
|
4848
|
+
trace_id: asyncCorrelation.traceId,
|
|
4849
|
+
orchestrator: "subagent",
|
|
4850
|
+
timeout_seconds: timeoutSeconds,
|
|
4851
|
+
status: "failed",
|
|
4852
|
+
error_code: "ASYNC_DISPATCH_FAILED",
|
|
4853
|
+
error_message: dispatchMessage
|
|
4854
|
+
});
|
|
4855
|
+
await emitOperationEventBestEffort(
|
|
4856
|
+
"operation.completed",
|
|
4857
|
+
{
|
|
4858
|
+
...eventContextBase,
|
|
4859
|
+
status: "failed",
|
|
4860
|
+
errorCode: "ASYNC_DISPATCH_FAILED",
|
|
4861
|
+
errorMessage: dispatchMessage
|
|
4862
|
+
},
|
|
4863
|
+
objectLogHomeDir
|
|
4864
|
+
);
|
|
4865
|
+
return {
|
|
4866
|
+
text: `Cannot dispatch subagent operation: ${dispatchMessage}
|
|
4867
|
+
operation-id: ${operationId}`,
|
|
4868
|
+
isError: true
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
await datastore.upsertOperation({
|
|
4873
|
+
operation_id: operationId,
|
|
4874
|
+
type: opType,
|
|
4875
|
+
object_id: opObject,
|
|
4876
|
+
quote_id: opQuote,
|
|
4877
|
+
trace_id: asyncCorrelation.traceId,
|
|
4878
|
+
orchestrator: "inline",
|
|
4879
|
+
status: "running",
|
|
4880
|
+
error_code: null,
|
|
4881
|
+
error_message: null
|
|
4882
|
+
});
|
|
4883
|
+
void runCloudCommandHandler(
|
|
4884
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4885
|
+
options,
|
|
4886
|
+
{
|
|
4887
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4888
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4889
|
+
}
|
|
4890
|
+
).then(async (result) => {
|
|
4154
4891
|
await datastore.upsertOperation({
|
|
4155
4892
|
operation_id: operationId,
|
|
4156
4893
|
type: opType,
|
|
4157
4894
|
object_id: opObject,
|
|
4158
4895
|
quote_id: opQuote,
|
|
4896
|
+
trace_id: asyncCorrelation.traceId,
|
|
4897
|
+
orchestrator: "inline",
|
|
4159
4898
|
status: result.isError ? "failed" : "succeeded",
|
|
4160
4899
|
error_code: result.isError ? "ASYNC_FAILED" : null,
|
|
4161
4900
|
error_message: result.isError ? result.text : null
|
|
4162
4901
|
});
|
|
4902
|
+
await emitOperationEventBestEffort(
|
|
4903
|
+
"operation.completed",
|
|
4904
|
+
{
|
|
4905
|
+
...eventContextBase,
|
|
4906
|
+
status: result.isError ? "failed" : "succeeded",
|
|
4907
|
+
errorCode: result.isError ? "ASYNC_FAILED" : null,
|
|
4908
|
+
errorMessage: result.isError ? result.text : null
|
|
4909
|
+
},
|
|
4910
|
+
objectLogHomeDir
|
|
4911
|
+
);
|
|
4163
4912
|
}).catch(async (err) => {
|
|
4913
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
4164
4914
|
await datastore.upsertOperation({
|
|
4165
4915
|
operation_id: operationId,
|
|
4166
4916
|
type: opType,
|
|
4167
4917
|
object_id: opObject,
|
|
4168
4918
|
quote_id: opQuote,
|
|
4919
|
+
trace_id: asyncCorrelation.traceId,
|
|
4920
|
+
orchestrator: "inline",
|
|
4169
4921
|
status: "failed",
|
|
4170
4922
|
error_code: "ASYNC_EXCEPTION",
|
|
4171
|
-
error_message:
|
|
4923
|
+
error_message: errorMessage
|
|
4172
4924
|
});
|
|
4925
|
+
await emitOperationEventBestEffort(
|
|
4926
|
+
"operation.completed",
|
|
4927
|
+
{
|
|
4928
|
+
...eventContextBase,
|
|
4929
|
+
status: "failed",
|
|
4930
|
+
errorCode: "ASYNC_EXCEPTION",
|
|
4931
|
+
errorMessage
|
|
4932
|
+
},
|
|
4933
|
+
objectLogHomeDir
|
|
4934
|
+
);
|
|
4173
4935
|
});
|
|
4174
4936
|
return {
|
|
4175
|
-
text:
|
|
4176
|
-
|
|
4937
|
+
text: [
|
|
4938
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4939
|
+
`orchestrator: inline`,
|
|
4940
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4941
|
+
].join("\n")
|
|
4177
4942
|
};
|
|
4178
4943
|
}
|
|
4179
4944
|
if (parsed.mode === "backup") {
|
|
@@ -4300,7 +5065,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4300
5065
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id) ?? await findLoggedPriceStorageQuote(parsed.uploadRequest.quote_id, objectLogHomeDir);
|
|
4301
5066
|
if (!loggedQuote) {
|
|
4302
5067
|
return {
|
|
4303
|
-
text: "Cannot upload storage object: quote-id not found in object.log. Run /
|
|
5068
|
+
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark_cloud price-storage first.",
|
|
4304
5069
|
isError: true
|
|
4305
5070
|
};
|
|
4306
5071
|
}
|
|
@@ -4319,7 +5084,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4319
5084
|
archiveStats = await stat2(archivePath);
|
|
4320
5085
|
} catch {
|
|
4321
5086
|
return {
|
|
4322
|
-
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /
|
|
5087
|
+
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark_cloud backup first.`,
|
|
4323
5088
|
isError: true
|
|
4324
5089
|
};
|
|
4325
5090
|
}
|
|
@@ -4452,18 +5217,50 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4452
5217
|
status: "active"
|
|
4453
5218
|
});
|
|
4454
5219
|
if (parsed.friendlyName?.trim()) {
|
|
5220
|
+
const normalizedFriendlyName = parsed.friendlyName.trim();
|
|
4455
5221
|
await datastore.upsertFriendlyName({
|
|
4456
|
-
friendly_name:
|
|
5222
|
+
friendly_name: normalizedFriendlyName,
|
|
4457
5223
|
object_id: finalizedUploadResponse.object_id,
|
|
4458
5224
|
object_key: finalizedUploadResponse.object_key,
|
|
4459
5225
|
quote_id: finalizedUploadResponse.quote_id,
|
|
4460
5226
|
wallet_address: finalizedUploadResponse.addr
|
|
4461
5227
|
});
|
|
5228
|
+
let friendlyNameVerified = false;
|
|
5229
|
+
try {
|
|
5230
|
+
const readBack = await datastore.resolveFriendlyName({
|
|
5231
|
+
walletAddress: finalizedUploadResponse.addr,
|
|
5232
|
+
friendlyName: normalizedFriendlyName,
|
|
5233
|
+
latest: true
|
|
5234
|
+
});
|
|
5235
|
+
friendlyNameVerified = Boolean(readBack?.objectKey) && readBack?.objectKey === finalizedUploadResponse.object_key;
|
|
5236
|
+
} catch {
|
|
5237
|
+
friendlyNameVerified = false;
|
|
5238
|
+
}
|
|
5239
|
+
if (!friendlyNameVerified) {
|
|
5240
|
+
const warning = "SQLite friendly-name write verification failed; manifest fallback may be required for --name lookups.";
|
|
5241
|
+
await emitCloudEventBestEffort(
|
|
5242
|
+
"friendly_name.write_verification_failed",
|
|
5243
|
+
{
|
|
5244
|
+
operation_id: uploadCorrelation.operationId,
|
|
5245
|
+
trace_id: uploadCorrelation.traceId,
|
|
5246
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
5247
|
+
object_id: finalizedUploadResponse.object_id,
|
|
5248
|
+
object_key: finalizedUploadResponse.object_key,
|
|
5249
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
5250
|
+
friendly_name: normalizedFriendlyName,
|
|
5251
|
+
warning
|
|
5252
|
+
},
|
|
5253
|
+
objectLogHomeDir
|
|
5254
|
+
);
|
|
5255
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
5256
|
+
throw new Error(warning);
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
4462
5259
|
try {
|
|
4463
5260
|
await appendJsonlEvent(
|
|
4464
5261
|
"manifest.jsonl",
|
|
4465
5262
|
{
|
|
4466
|
-
friendly_name:
|
|
5263
|
+
friendly_name: normalizedFriendlyName,
|
|
4467
5264
|
object_id: finalizedUploadResponse.object_id,
|
|
4468
5265
|
object_key: finalizedUploadResponse.object_key,
|
|
4469
5266
|
quote_id: finalizedUploadResponse.quote_id,
|
|
@@ -4516,12 +5313,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4516
5313
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4517
5314
|
datastore,
|
|
4518
5315
|
parsed.storageObjectRequest,
|
|
4519
|
-
parsed.nameSelector
|
|
5316
|
+
parsed.nameSelector,
|
|
5317
|
+
objectLogHomeDir
|
|
4520
5318
|
);
|
|
4521
5319
|
if (resolved.error || !resolved.request) {
|
|
4522
5320
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4523
5321
|
}
|
|
4524
5322
|
const resolvedRequest = resolved.request;
|
|
5323
|
+
if (resolved.degradedWarning) {
|
|
5324
|
+
await emitCloudEventBestEffort(
|
|
5325
|
+
"name_resolution.degraded",
|
|
5326
|
+
{
|
|
5327
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5328
|
+
object_key: resolvedRequest.object_key,
|
|
5329
|
+
warning: resolved.degradedWarning
|
|
5330
|
+
},
|
|
5331
|
+
objectLogHomeDir
|
|
5332
|
+
);
|
|
5333
|
+
}
|
|
4525
5334
|
const correlation = buildRequestCorrelation();
|
|
4526
5335
|
const operationId = correlation.operationId;
|
|
4527
5336
|
const knownObject = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
@@ -4563,8 +5372,10 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4563
5372
|
},
|
|
4564
5373
|
objectLogHomeDir
|
|
4565
5374
|
);
|
|
5375
|
+
const lsText = formatStorageLsUserMessage(lsResult, resolvedRequest.object_key);
|
|
4566
5376
|
return {
|
|
4567
|
-
text:
|
|
5377
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5378
|
+
${lsText}` : lsText
|
|
4568
5379
|
};
|
|
4569
5380
|
} catch {
|
|
4570
5381
|
await datastore.upsertOperation({
|
|
@@ -4597,12 +5408,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4597
5408
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4598
5409
|
datastore,
|
|
4599
5410
|
parsed.storageObjectRequest,
|
|
4600
|
-
parsed.nameSelector
|
|
5411
|
+
parsed.nameSelector,
|
|
5412
|
+
objectLogHomeDir
|
|
4601
5413
|
);
|
|
4602
5414
|
if (resolved.error || !resolved.request) {
|
|
4603
5415
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4604
5416
|
}
|
|
4605
5417
|
const resolvedRequest = resolved.request;
|
|
5418
|
+
if (resolved.degradedWarning) {
|
|
5419
|
+
await emitCloudEventBestEffort(
|
|
5420
|
+
"name_resolution.degraded",
|
|
5421
|
+
{
|
|
5422
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5423
|
+
object_key: resolvedRequest.object_key,
|
|
5424
|
+
warning: resolved.degradedWarning
|
|
5425
|
+
},
|
|
5426
|
+
objectLogHomeDir
|
|
5427
|
+
);
|
|
5428
|
+
}
|
|
4606
5429
|
const correlation = buildRequestCorrelation(
|
|
4607
5430
|
executionContext.forcedOperationId,
|
|
4608
5431
|
executionContext.forcedTraceId
|
|
@@ -4647,8 +5470,10 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4647
5470
|
},
|
|
4648
5471
|
objectLogHomeDir
|
|
4649
5472
|
);
|
|
5473
|
+
const downloadText = `File ${resolvedRequest.object_key} downloaded to ${downloadResult.file_path}`;
|
|
4650
5474
|
return {
|
|
4651
|
-
text:
|
|
5475
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5476
|
+
${downloadText}` : downloadText
|
|
4652
5477
|
};
|
|
4653
5478
|
} catch {
|
|
4654
5479
|
await datastore.upsertOperation({
|
|
@@ -4681,12 +5506,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4681
5506
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4682
5507
|
datastore,
|
|
4683
5508
|
parsed.storageObjectRequest,
|
|
4684
|
-
parsed.nameSelector
|
|
5509
|
+
parsed.nameSelector,
|
|
5510
|
+
objectLogHomeDir
|
|
4685
5511
|
);
|
|
4686
5512
|
if (resolved.error || !resolved.request) {
|
|
4687
5513
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4688
5514
|
}
|
|
4689
5515
|
const resolvedRequest = resolved.request;
|
|
5516
|
+
if (resolved.degradedWarning) {
|
|
5517
|
+
await emitCloudEventBestEffort(
|
|
5518
|
+
"name_resolution.degraded",
|
|
5519
|
+
{
|
|
5520
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5521
|
+
object_key: resolvedRequest.object_key,
|
|
5522
|
+
warning: resolved.degradedWarning
|
|
5523
|
+
},
|
|
5524
|
+
objectLogHomeDir
|
|
5525
|
+
);
|
|
5526
|
+
}
|
|
4690
5527
|
const correlation = buildRequestCorrelation();
|
|
4691
5528
|
const operationId = correlation.operationId;
|
|
4692
5529
|
const existingObjectByKey = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
@@ -4768,12 +5605,14 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4768
5605
|
},
|
|
4769
5606
|
objectLogHomeDir
|
|
4770
5607
|
);
|
|
5608
|
+
const deleteText = formatStorageDeleteUserMessage(
|
|
5609
|
+
resolvedRequest.object_key,
|
|
5610
|
+
cronEntry?.cronId ?? null,
|
|
5611
|
+
cronDeleted
|
|
5612
|
+
);
|
|
4771
5613
|
return {
|
|
4772
|
-
text:
|
|
4773
|
-
|
|
4774
|
-
cronEntry?.cronId ?? null,
|
|
4775
|
-
cronDeleted
|
|
4776
|
-
)
|
|
5614
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5615
|
+
${deleteText}` : deleteText
|
|
4777
5616
|
};
|
|
4778
5617
|
}
|
|
4779
5618
|
return {
|