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/index.js
CHANGED
|
@@ -1766,7 +1766,7 @@ async function startProxy(options) {
|
|
|
1766
1766
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1767
1767
|
sendJson(res, 400, {
|
|
1768
1768
|
error: "Bad request",
|
|
1769
|
-
message: "Invalid JSON body for /
|
|
1769
|
+
message: "Invalid JSON body for /mnemospark_cloud price-storage"
|
|
1770
1770
|
});
|
|
1771
1771
|
return;
|
|
1772
1772
|
}
|
|
@@ -1834,7 +1834,7 @@ async function startProxy(options) {
|
|
|
1834
1834
|
});
|
|
1835
1835
|
sendJson(res, 502, {
|
|
1836
1836
|
error: "proxy_error",
|
|
1837
|
-
message: `Failed to forward /
|
|
1837
|
+
message: `Failed to forward /mnemospark_cloud price-storage: ${err instanceof Error ? err.message : String(err)}`
|
|
1838
1838
|
});
|
|
1839
1839
|
}
|
|
1840
1840
|
return;
|
|
@@ -1980,7 +1980,7 @@ async function startProxy(options) {
|
|
|
1980
1980
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
1981
1981
|
sendJson(res, 400, {
|
|
1982
1982
|
error: "Bad request",
|
|
1983
|
-
message: "Invalid JSON body for /
|
|
1983
|
+
message: "Invalid JSON body for /mnemospark_cloud upload"
|
|
1984
1984
|
});
|
|
1985
1985
|
return;
|
|
1986
1986
|
}
|
|
@@ -2043,7 +2043,7 @@ async function startProxy(options) {
|
|
|
2043
2043
|
error: "insufficient_balance",
|
|
2044
2044
|
message: `Insufficient USDC balance. Current: ${sufficiency.info.balanceUSD}, Required: ${requiredUSD}`,
|
|
2045
2045
|
wallet: requestPayload.wallet_address,
|
|
2046
|
-
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /
|
|
2046
|
+
help: `Fund wallet ${requestPayload.wallet_address} on Base before running /mnemospark_cloud upload`
|
|
2047
2047
|
});
|
|
2048
2048
|
return;
|
|
2049
2049
|
}
|
|
@@ -2151,7 +2151,7 @@ async function startProxy(options) {
|
|
|
2151
2151
|
});
|
|
2152
2152
|
sendJson(res, 502, {
|
|
2153
2153
|
error: "proxy_error",
|
|
2154
|
-
message: `Failed to forward /
|
|
2154
|
+
message: `Failed to forward /mnemospark_cloud upload: ${err instanceof Error ? err.message : String(err)}`
|
|
2155
2155
|
});
|
|
2156
2156
|
}
|
|
2157
2157
|
return;
|
|
@@ -2169,7 +2169,7 @@ async function startProxy(options) {
|
|
|
2169
2169
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2170
2170
|
sendJson(res, 400, {
|
|
2171
2171
|
error: "Bad request",
|
|
2172
|
-
message: "Invalid JSON body for /
|
|
2172
|
+
message: "Invalid JSON body for /mnemospark_cloud upload/confirm"
|
|
2173
2173
|
});
|
|
2174
2174
|
return;
|
|
2175
2175
|
}
|
|
@@ -2250,7 +2250,7 @@ async function startProxy(options) {
|
|
|
2250
2250
|
});
|
|
2251
2251
|
sendJson(res, 502, {
|
|
2252
2252
|
error: "proxy_error",
|
|
2253
|
-
message: `Failed to forward /
|
|
2253
|
+
message: `Failed to forward /mnemospark_cloud upload/confirm: ${err instanceof Error ? err.message : String(err)}`
|
|
2254
2254
|
});
|
|
2255
2255
|
}
|
|
2256
2256
|
return;
|
|
@@ -2268,7 +2268,7 @@ async function startProxy(options) {
|
|
|
2268
2268
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2269
2269
|
sendJson(res, 400, {
|
|
2270
2270
|
error: "Bad request",
|
|
2271
|
-
message: "Invalid JSON body for /
|
|
2271
|
+
message: "Invalid JSON body for /mnemospark_cloud ls"
|
|
2272
2272
|
});
|
|
2273
2273
|
return;
|
|
2274
2274
|
}
|
|
@@ -2341,7 +2341,7 @@ async function startProxy(options) {
|
|
|
2341
2341
|
});
|
|
2342
2342
|
sendJson(res, 502, {
|
|
2343
2343
|
error: "proxy_error",
|
|
2344
|
-
message: `Failed to forward /
|
|
2344
|
+
message: `Failed to forward /mnemospark_cloud ls: ${err instanceof Error ? err.message : String(err)}`
|
|
2345
2345
|
});
|
|
2346
2346
|
}
|
|
2347
2347
|
return;
|
|
@@ -2361,7 +2361,7 @@ async function startProxy(options) {
|
|
|
2361
2361
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2362
2362
|
sendJson(res, 400, {
|
|
2363
2363
|
error: "Bad request",
|
|
2364
|
-
message: "Invalid JSON body for /
|
|
2364
|
+
message: "Invalid JSON body for /mnemospark_cloud download"
|
|
2365
2365
|
});
|
|
2366
2366
|
return;
|
|
2367
2367
|
}
|
|
@@ -2459,7 +2459,7 @@ async function startProxy(options) {
|
|
|
2459
2459
|
});
|
|
2460
2460
|
sendJson(res, 502, {
|
|
2461
2461
|
error: "proxy_error",
|
|
2462
|
-
message: `Failed to forward /
|
|
2462
|
+
message: `Failed to forward /mnemospark_cloud download: ${err instanceof Error ? err.message : String(err)}`
|
|
2463
2463
|
});
|
|
2464
2464
|
}
|
|
2465
2465
|
return;
|
|
@@ -2477,7 +2477,7 @@ async function startProxy(options) {
|
|
|
2477
2477
|
emitProxyTerminalFromStatus(correlation, 400, { reason: "invalid_json" });
|
|
2478
2478
|
sendJson(res, 400, {
|
|
2479
2479
|
error: "Bad request",
|
|
2480
|
-
message: "Invalid JSON body for /
|
|
2480
|
+
message: "Invalid JSON body for /mnemospark_cloud delete"
|
|
2481
2481
|
});
|
|
2482
2482
|
return;
|
|
2483
2483
|
}
|
|
@@ -2554,7 +2554,7 @@ async function startProxy(options) {
|
|
|
2554
2554
|
});
|
|
2555
2555
|
sendJson(res, 502, {
|
|
2556
2556
|
error: "proxy_error",
|
|
2557
|
-
message: `Failed to forward /
|
|
2557
|
+
message: `Failed to forward /mnemospark_cloud delete: ${err instanceof Error ? err.message : String(err)}`
|
|
2558
2558
|
});
|
|
2559
2559
|
}
|
|
2560
2560
|
return;
|
|
@@ -2806,7 +2806,7 @@ import { mkdir as mkdir4 } from "fs/promises";
|
|
|
2806
2806
|
import { homedir as homedir5 } from "os";
|
|
2807
2807
|
import { dirname as dirname4, join as join7 } from "path";
|
|
2808
2808
|
var DB_SUBPATH = join7(".openclaw", "mnemospark", "state.db");
|
|
2809
|
-
var SCHEMA_VERSION =
|
|
2809
|
+
var SCHEMA_VERSION = 3;
|
|
2810
2810
|
function resolveDbPath(homeDir) {
|
|
2811
2811
|
return join7(homeDir ?? homedir5(), DB_SUBPATH);
|
|
2812
2812
|
}
|
|
@@ -2912,6 +2912,21 @@ async function createCloudDatastore(homeDir) {
|
|
|
2912
2912
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_wallet ON friendly_names(wallet_address);
|
|
2913
2913
|
CREATE INDEX IF NOT EXISTS idx_friendly_names_created_at ON friendly_names(created_at);
|
|
2914
2914
|
`);
|
|
2915
|
+
const addOperationsColumn = (columnName, sqlType) => {
|
|
2916
|
+
try {
|
|
2917
|
+
nextDb.exec(`ALTER TABLE operations ADD COLUMN ${columnName} ${sqlType}`);
|
|
2918
|
+
} catch (error) {
|
|
2919
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error);
|
|
2920
|
+
if (!message.includes("duplicate column name")) {
|
|
2921
|
+
throw error;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2925
|
+
addOperationsColumn("trace_id", "TEXT");
|
|
2926
|
+
addOperationsColumn("orchestrator", "TEXT");
|
|
2927
|
+
addOperationsColumn("subagent_session_id", "TEXT");
|
|
2928
|
+
addOperationsColumn("timeout_seconds", "INTEGER");
|
|
2929
|
+
addOperationsColumn("cancel_requested_at", "TEXT");
|
|
2915
2930
|
nextDb.prepare(
|
|
2916
2931
|
`INSERT INTO schema_migrations(version, applied_at)
|
|
2917
2932
|
VALUES(?, ?)
|
|
@@ -2923,7 +2938,10 @@ async function createCloudDatastore(homeDir) {
|
|
|
2923
2938
|
try {
|
|
2924
2939
|
await ensureReady();
|
|
2925
2940
|
return fn();
|
|
2926
|
-
} catch {
|
|
2941
|
+
} catch (error) {
|
|
2942
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
2943
|
+
throw error;
|
|
2944
|
+
}
|
|
2927
2945
|
return fallback;
|
|
2928
2946
|
}
|
|
2929
2947
|
};
|
|
@@ -3048,13 +3066,19 @@ async function createCloudDatastore(homeDir) {
|
|
|
3048
3066
|
upsertOperation: async (row) => {
|
|
3049
3067
|
await safe(() => {
|
|
3050
3068
|
const ts = nowIso();
|
|
3069
|
+
const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
3051
3070
|
db.prepare(
|
|
3052
|
-
`INSERT INTO operations(operation_id, type, object_id, quote_id, status, error_code, error_message, started_at, finished_at, created_at, updated_at)
|
|
3053
|
-
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3071
|
+
`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)
|
|
3072
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3054
3073
|
ON CONFLICT(operation_id) DO UPDATE SET
|
|
3055
3074
|
type=excluded.type,
|
|
3056
3075
|
object_id=COALESCE(excluded.object_id, operations.object_id),
|
|
3057
|
-
quote_id=excluded.quote_id,
|
|
3076
|
+
quote_id=COALESCE(excluded.quote_id, operations.quote_id),
|
|
3077
|
+
trace_id=COALESCE(excluded.trace_id, operations.trace_id),
|
|
3078
|
+
orchestrator=COALESCE(excluded.orchestrator, operations.orchestrator),
|
|
3079
|
+
subagent_session_id=COALESCE(excluded.subagent_session_id, operations.subagent_session_id),
|
|
3080
|
+
timeout_seconds=COALESCE(excluded.timeout_seconds, operations.timeout_seconds),
|
|
3081
|
+
cancel_requested_at=COALESCE(excluded.cancel_requested_at, operations.cancel_requested_at),
|
|
3058
3082
|
status=excluded.status,
|
|
3059
3083
|
error_code=excluded.error_code,
|
|
3060
3084
|
error_message=excluded.error_message,
|
|
@@ -3066,11 +3090,16 @@ async function createCloudDatastore(homeDir) {
|
|
|
3066
3090
|
row.type,
|
|
3067
3091
|
row.object_id,
|
|
3068
3092
|
row.quote_id,
|
|
3093
|
+
row.trace_id ?? null,
|
|
3094
|
+
row.orchestrator ?? null,
|
|
3095
|
+
row.subagent_session_id ?? null,
|
|
3096
|
+
row.timeout_seconds ?? null,
|
|
3097
|
+
row.cancel_requested_at ?? null,
|
|
3069
3098
|
row.status,
|
|
3070
3099
|
row.error_code,
|
|
3071
3100
|
row.error_message,
|
|
3072
3101
|
row.status === "started" ? ts : null,
|
|
3073
|
-
|
|
3102
|
+
terminalStatuses.has(row.status) ? ts : null,
|
|
3074
3103
|
ts,
|
|
3075
3104
|
ts
|
|
3076
3105
|
);
|
|
@@ -3078,7 +3107,7 @@ async function createCloudDatastore(homeDir) {
|
|
|
3078
3107
|
},
|
|
3079
3108
|
findOperationById: async (operationId) => safe(() => {
|
|
3080
3109
|
const row = db.prepare(
|
|
3081
|
-
`SELECT operation_id, type, status, error_code, error_message, started_at, finished_at, updated_at
|
|
3110
|
+
`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
|
|
3082
3111
|
FROM operations
|
|
3083
3112
|
WHERE operation_id = ?
|
|
3084
3113
|
LIMIT 1`
|
|
@@ -3180,7 +3209,9 @@ var REQUIRED_UPLOAD = "--quote-id, --wallet-address, --object-id, --object-id-ha
|
|
|
3180
3209
|
var REQUIRED_STORAGE_OBJECT = "--wallet-address and one of (--object-key | --name [--latest|--at])";
|
|
3181
3210
|
var BOOLEAN_SELECTOR_FLAGS = /* @__PURE__ */ new Set(["latest"]);
|
|
3182
3211
|
var BOOLEAN_ASYNC_FLAGS = /* @__PURE__ */ new Set(["async"]);
|
|
3212
|
+
var BOOLEAN_OP_STATUS_FLAGS = /* @__PURE__ */ new Set(["cancel"]);
|
|
3183
3213
|
var BOOLEAN_SELECTOR_AND_ASYNC_FLAGS = /* @__PURE__ */ new Set(["latest", "async"]);
|
|
3214
|
+
var ORCHESTRATOR_MODES = /* @__PURE__ */ new Set(["inline", "subagent"]);
|
|
3184
3215
|
function expandTilde(path) {
|
|
3185
3216
|
const trimmed = path.trim();
|
|
3186
3217
|
if (trimmed === "~") {
|
|
@@ -3194,29 +3225,54 @@ function expandTilde(path) {
|
|
|
3194
3225
|
var CLOUD_HELP_TEXT = [
|
|
3195
3226
|
"\u2601\uFE0F **mnemospark Cloud Commands**",
|
|
3196
3227
|
"",
|
|
3197
|
-
"\u2022 `/
|
|
3228
|
+
"\u2022 `/mnemospark_cloud` or `/mnemospark_cloud help` \u2014 show this message",
|
|
3198
3229
|
"",
|
|
3199
|
-
"\u2022 `/
|
|
3200
|
-
"
|
|
3230
|
+
"\u2022 `/mnemospark_cloud backup <file|directory> [--name <friendly-name>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3231
|
+
" Purpose: create a local tar+gzip backup object and index it for later upload.",
|
|
3232
|
+
" Required: <file|directory>",
|
|
3201
3233
|
"",
|
|
3202
|
-
"\u2022 `/
|
|
3234
|
+
"\u2022 `/mnemospark_cloud price-storage --wallet-address <addr> --object-id <id> --object-id-hash <hash> --gb <gb> --provider <provider> --region <region>`",
|
|
3235
|
+
" Purpose: request a storage quote before upload.",
|
|
3203
3236
|
" Required: " + REQUIRED_PRICE_STORAGE,
|
|
3204
3237
|
"",
|
|
3205
|
-
"\u2022 `/
|
|
3238
|
+
"\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>]`",
|
|
3239
|
+
" Purpose: upload an encrypted object using a valid quote-id.",
|
|
3206
3240
|
" Required: " + REQUIRED_UPLOAD,
|
|
3207
3241
|
"",
|
|
3208
|
-
"\u2022 `/
|
|
3242
|
+
"\u2022 `/mnemospark_cloud ls --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3243
|
+
" Purpose: look up remote object metadata.",
|
|
3209
3244
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3210
3245
|
"",
|
|
3211
|
-
"\u2022 `/
|
|
3246
|
+
"\u2022 `/mnemospark_cloud download --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>] [--async] [--orchestrator <inline|subagent>] [--timeout-seconds <n>]`",
|
|
3247
|
+
" Purpose: fetch an object to local disk.",
|
|
3212
3248
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3213
3249
|
"",
|
|
3214
|
-
"\u2022 `/
|
|
3250
|
+
"\u2022 `/mnemospark_cloud delete --wallet-address <addr> [--object-key <object-key> | --name <friendly-name>] [--latest|--at <timestamp>]`",
|
|
3251
|
+
" Purpose: remove a remote object and local cron tracking when present.",
|
|
3215
3252
|
" Required: " + REQUIRED_STORAGE_OBJECT,
|
|
3216
3253
|
"",
|
|
3217
|
-
"\u2022 `/
|
|
3254
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> [--cancel]`",
|
|
3255
|
+
" Purpose: inspect async operation status, or request cancellation for subagent runs.",
|
|
3218
3256
|
" Required: --operation-id",
|
|
3219
3257
|
"",
|
|
3258
|
+
"Async orchestration flags (`backup`, `upload`, `download` only):",
|
|
3259
|
+
"\u2022 `--async`",
|
|
3260
|
+
" Start operation in background and return quickly with operation-id.",
|
|
3261
|
+
"\u2022 `--orchestrator <inline|subagent>`",
|
|
3262
|
+
" Choose async engine. Default when omitted is `inline`.",
|
|
3263
|
+
" Use `subagent` for explicit subagent session tracking and cancellation.",
|
|
3264
|
+
"\u2022 `--timeout-seconds <n>`",
|
|
3265
|
+
" Optional per-operation timeout. Valid only with `--async --orchestrator subagent`.",
|
|
3266
|
+
" `n` must be a positive integer (seconds).",
|
|
3267
|
+
"\u2022 `op-status --cancel`",
|
|
3268
|
+
" Cancel a subagent-orchestrated operation by operation-id (idempotent).",
|
|
3269
|
+
"",
|
|
3270
|
+
"Examples:",
|
|
3271
|
+
"\u2022 `/mnemospark_cloud upload ... --async --orchestrator subagent`",
|
|
3272
|
+
"\u2022 `/mnemospark_cloud download ... --async --orchestrator subagent --timeout-seconds 900`",
|
|
3273
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id>`",
|
|
3274
|
+
"\u2022 `/mnemospark_cloud op-status --operation-id <id> --cancel`",
|
|
3275
|
+
"",
|
|
3220
3276
|
"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."
|
|
3221
3277
|
].join("\n");
|
|
3222
3278
|
var UnsupportedBackupPlatformError = class extends Error {
|
|
@@ -3306,9 +3362,73 @@ function parseStorageObjectRequestInput(flags, selector) {
|
|
|
3306
3362
|
location
|
|
3307
3363
|
});
|
|
3308
3364
|
}
|
|
3309
|
-
function
|
|
3365
|
+
function parseOrchestratorMode(value) {
|
|
3366
|
+
if (!value) {
|
|
3367
|
+
return void 0;
|
|
3368
|
+
}
|
|
3369
|
+
const normalized = value.trim().toLowerCase();
|
|
3370
|
+
if (!normalized) {
|
|
3371
|
+
return null;
|
|
3372
|
+
}
|
|
3373
|
+
if (!ORCHESTRATOR_MODES.has(normalized)) {
|
|
3374
|
+
return null;
|
|
3375
|
+
}
|
|
3376
|
+
return normalized;
|
|
3377
|
+
}
|
|
3378
|
+
function parseTimeoutSeconds(value) {
|
|
3379
|
+
if (!value) {
|
|
3380
|
+
return void 0;
|
|
3381
|
+
}
|
|
3382
|
+
const trimmed = value.trim();
|
|
3383
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
3384
|
+
return null;
|
|
3385
|
+
}
|
|
3386
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
3387
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3388
|
+
return null;
|
|
3389
|
+
}
|
|
3390
|
+
return parsed;
|
|
3391
|
+
}
|
|
3392
|
+
function parseAsyncOperationArgs(flags) {
|
|
3393
|
+
const asyncRequested = flags.async === "true";
|
|
3394
|
+
const hasOrchestratorFlag = typeof flags.orchestrator === "string";
|
|
3395
|
+
const hasTimeoutFlag = typeof flags["timeout-seconds"] === "string";
|
|
3396
|
+
if (!asyncRequested && (hasOrchestratorFlag || hasTimeoutFlag)) {
|
|
3397
|
+
return null;
|
|
3398
|
+
}
|
|
3399
|
+
const parsedOrchestrator = parseOrchestratorMode(flags.orchestrator);
|
|
3400
|
+
if (parsedOrchestrator === null) {
|
|
3401
|
+
return null;
|
|
3402
|
+
}
|
|
3403
|
+
const parsedTimeoutSeconds = parseTimeoutSeconds(flags["timeout-seconds"]);
|
|
3404
|
+
if (parsedTimeoutSeconds === null) {
|
|
3405
|
+
return null;
|
|
3406
|
+
}
|
|
3407
|
+
if (typeof parsedTimeoutSeconds === "number" && (parsedOrchestrator ?? "inline") !== "subagent") {
|
|
3408
|
+
return null;
|
|
3409
|
+
}
|
|
3410
|
+
return {
|
|
3411
|
+
async: asyncRequested,
|
|
3412
|
+
orchestrator: parsedOrchestrator === void 0 ? void 0 : parsedOrchestrator,
|
|
3413
|
+
timeoutSeconds: parsedTimeoutSeconds === void 0 ? void 0 : parsedTimeoutSeconds
|
|
3414
|
+
};
|
|
3415
|
+
}
|
|
3416
|
+
var INVALID_ASYNC_FLAGS_MESSAGE = "invalid async flags. `--orchestrator`/`--timeout-seconds` require `--async`, and `--timeout-seconds` is only valid with `--orchestrator subagent`.";
|
|
3417
|
+
function stripAsyncControlFlags(args) {
|
|
3310
3418
|
const tokens = tokenizeArgsRaw(args ?? "");
|
|
3311
|
-
const filtered =
|
|
3419
|
+
const filtered = [];
|
|
3420
|
+
for (let idx = 0; idx < tokens.length; idx += 1) {
|
|
3421
|
+
const token = tokens[idx];
|
|
3422
|
+
const lowerToken = token.toLowerCase();
|
|
3423
|
+
if (lowerToken === "--async") {
|
|
3424
|
+
continue;
|
|
3425
|
+
}
|
|
3426
|
+
if (lowerToken === "--orchestrator" || lowerToken === "--timeout-seconds") {
|
|
3427
|
+
idx += 1;
|
|
3428
|
+
continue;
|
|
3429
|
+
}
|
|
3430
|
+
filtered.push(token);
|
|
3431
|
+
}
|
|
3312
3432
|
return filtered.join(" ");
|
|
3313
3433
|
}
|
|
3314
3434
|
function parseCloudArgs(args) {
|
|
@@ -3331,8 +3451,21 @@ function parseCloudArgs(args) {
|
|
|
3331
3451
|
if (!backupTarget) {
|
|
3332
3452
|
return { mode: "unknown" };
|
|
3333
3453
|
}
|
|
3334
|
-
const
|
|
3335
|
-
|
|
3454
|
+
const remainingTokens = tokens.slice(1);
|
|
3455
|
+
const flags = remainingTokens.length === 0 ? {} : parseNamedFlagsTokens(remainingTokens, BOOLEAN_ASYNC_FLAGS);
|
|
3456
|
+
if (!flags) {
|
|
3457
|
+
return { mode: "backup-invalid" };
|
|
3458
|
+
}
|
|
3459
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3460
|
+
if (!asyncArgs) {
|
|
3461
|
+
return { mode: "backup-invalid-async" };
|
|
3462
|
+
}
|
|
3463
|
+
return {
|
|
3464
|
+
mode: "backup",
|
|
3465
|
+
backupTarget,
|
|
3466
|
+
friendlyName: flags.name?.trim() || void 0,
|
|
3467
|
+
...asyncArgs
|
|
3468
|
+
};
|
|
3336
3469
|
}
|
|
3337
3470
|
if (subcommand === "price-storage") {
|
|
3338
3471
|
const flags = parseNamedFlags(rest);
|
|
@@ -3358,6 +3491,10 @@ function parseCloudArgs(args) {
|
|
|
3358
3491
|
if (!flags) {
|
|
3359
3492
|
return { mode: "upload-invalid" };
|
|
3360
3493
|
}
|
|
3494
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3495
|
+
if (!asyncArgs) {
|
|
3496
|
+
return { mode: "upload-invalid-async" };
|
|
3497
|
+
}
|
|
3361
3498
|
const quoteId = flags["quote-id"]?.trim();
|
|
3362
3499
|
const walletAddress = flags["wallet-address"]?.trim();
|
|
3363
3500
|
const objectId = flags["object-id"]?.trim();
|
|
@@ -3368,7 +3505,7 @@ function parseCloudArgs(args) {
|
|
|
3368
3505
|
return {
|
|
3369
3506
|
mode: "upload",
|
|
3370
3507
|
friendlyName: flags.name?.trim() || void 0,
|
|
3371
|
-
|
|
3508
|
+
...asyncArgs,
|
|
3372
3509
|
uploadRequest: {
|
|
3373
3510
|
quote_id: quoteId,
|
|
3374
3511
|
wallet_address: walletAddress,
|
|
@@ -3397,6 +3534,10 @@ function parseCloudArgs(args) {
|
|
|
3397
3534
|
if (!flags) {
|
|
3398
3535
|
return { mode: "download-invalid" };
|
|
3399
3536
|
}
|
|
3537
|
+
const asyncArgs = parseAsyncOperationArgs(flags);
|
|
3538
|
+
if (!asyncArgs) {
|
|
3539
|
+
return { mode: "download-invalid-async" };
|
|
3540
|
+
}
|
|
3400
3541
|
const selector = parseObjectSelector(flags);
|
|
3401
3542
|
if (!selector) {
|
|
3402
3543
|
return { mode: "download-invalid" };
|
|
@@ -3409,7 +3550,7 @@ function parseCloudArgs(args) {
|
|
|
3409
3550
|
mode: "download",
|
|
3410
3551
|
storageObjectRequest: request,
|
|
3411
3552
|
nameSelector: selector.nameSelector,
|
|
3412
|
-
|
|
3553
|
+
...asyncArgs
|
|
3413
3554
|
};
|
|
3414
3555
|
}
|
|
3415
3556
|
if (subcommand === "delete") {
|
|
@@ -3428,12 +3569,12 @@ function parseCloudArgs(args) {
|
|
|
3428
3569
|
return { mode: "delete", storageObjectRequest: request, nameSelector: selector.nameSelector };
|
|
3429
3570
|
}
|
|
3430
3571
|
if (subcommand === "op-status") {
|
|
3431
|
-
const flags = parseNamedFlags(rest);
|
|
3572
|
+
const flags = parseNamedFlags(rest, BOOLEAN_OP_STATUS_FLAGS);
|
|
3432
3573
|
const operationId = flags?.["operation-id"]?.trim();
|
|
3433
3574
|
if (!operationId) {
|
|
3434
3575
|
return { mode: "op-status-invalid" };
|
|
3435
3576
|
}
|
|
3436
|
-
return { mode: "op-status", operationId };
|
|
3577
|
+
return { mode: "op-status", operationId, cancel: flags?.cancel === "true" };
|
|
3437
3578
|
}
|
|
3438
3579
|
return { mode: "unknown" };
|
|
3439
3580
|
}
|
|
@@ -3924,17 +4065,37 @@ async function uploadPresignedObjectIfNeeded(uploadResponse, uploadMode, encrypt
|
|
|
3924
4065
|
if (!headers.has("content-type")) {
|
|
3925
4066
|
headers.set("content-type", "application/octet-stream");
|
|
3926
4067
|
}
|
|
3927
|
-
const
|
|
4068
|
+
const putBody = new Uint8Array(encryptedContent);
|
|
4069
|
+
const firstAttempt = await fetchImpl(uploadResponse.upload_url, {
|
|
3928
4070
|
method: "PUT",
|
|
3929
4071
|
headers,
|
|
3930
|
-
body:
|
|
4072
|
+
body: putBody,
|
|
4073
|
+
redirect: "manual"
|
|
3931
4074
|
});
|
|
3932
|
-
if (
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
);
|
|
4075
|
+
if (firstAttempt.ok) {
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
if ((firstAttempt.status === 307 || firstAttempt.status === 308) && firstAttempt.headers.has("location")) {
|
|
4079
|
+
const location = firstAttempt.headers.get("location")?.trim();
|
|
4080
|
+
if (location) {
|
|
4081
|
+
const redirectedAttempt = await fetchImpl(location, {
|
|
4082
|
+
method: "PUT",
|
|
4083
|
+
headers,
|
|
4084
|
+
body: putBody
|
|
4085
|
+
});
|
|
4086
|
+
if (redirectedAttempt.ok) {
|
|
4087
|
+
return;
|
|
4088
|
+
}
|
|
4089
|
+
const redirectedDetails = (await redirectedAttempt.text()).trim();
|
|
4090
|
+
throw new Error(
|
|
4091
|
+
`Presigned upload failed after redirect with status ${redirectedAttempt.status}${redirectedDetails ? `: ${redirectedDetails}` : ""}`
|
|
4092
|
+
);
|
|
4093
|
+
}
|
|
3937
4094
|
}
|
|
4095
|
+
const details = (await firstAttempt.text()).trim();
|
|
4096
|
+
throw new Error(
|
|
4097
|
+
`Presigned upload failed with status ${firstAttempt.status}${details ? `: ${details}` : ""}`
|
|
4098
|
+
);
|
|
3938
4099
|
}
|
|
3939
4100
|
async function appendStorageUploadLog(upload, homeDir, nowDateFn = () => /* @__PURE__ */ new Date()) {
|
|
3940
4101
|
return appendObjectLogLine(
|
|
@@ -4005,16 +4166,118 @@ function extractUploadErrorMessage(error) {
|
|
|
4005
4166
|
function formatPriceStorageUserMessage(quote) {
|
|
4006
4167
|
return [
|
|
4007
4168
|
`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}\``,
|
|
4008
|
-
`If you accept this quote run the command /
|
|
4169
|
+
`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}\``
|
|
4009
4170
|
].join("\n");
|
|
4010
4171
|
}
|
|
4011
4172
|
function formatStorageLsUserMessage(result, requestedObjectKey) {
|
|
4012
4173
|
const objectId = result.object_id ?? result.key;
|
|
4013
4174
|
return `${objectId} with ${requestedObjectKey} is ${result.size_bytes} in ${result.bucket}`;
|
|
4014
4175
|
}
|
|
4176
|
+
function createInProcessSubagentOrchestrator() {
|
|
4177
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
4178
|
+
const completeSession = async (sessionId, handler) => {
|
|
4179
|
+
const session = sessions.get(sessionId);
|
|
4180
|
+
if (!session || session.terminal) {
|
|
4181
|
+
return false;
|
|
4182
|
+
}
|
|
4183
|
+
session.terminal = true;
|
|
4184
|
+
if (session.timeoutHandle) {
|
|
4185
|
+
clearTimeout(session.timeoutHandle);
|
|
4186
|
+
}
|
|
4187
|
+
sessions.delete(sessionId);
|
|
4188
|
+
await handler(session.hooks);
|
|
4189
|
+
return true;
|
|
4190
|
+
};
|
|
4191
|
+
return {
|
|
4192
|
+
dispatch: async (input) => {
|
|
4193
|
+
const sessionId = `agent:mnemospark:subagent:${randomUUID3()}`;
|
|
4194
|
+
const state = {
|
|
4195
|
+
terminal: false,
|
|
4196
|
+
cancelRequested: false,
|
|
4197
|
+
hooks: input.hooks
|
|
4198
|
+
};
|
|
4199
|
+
sessions.set(sessionId, state);
|
|
4200
|
+
if (typeof input.timeoutSeconds === "number" && input.timeoutSeconds > 0) {
|
|
4201
|
+
state.timeoutHandle = setTimeout(() => {
|
|
4202
|
+
void completeSession(sessionId, async (hooks) => {
|
|
4203
|
+
await hooks?.onTimedOut?.(sessionId);
|
|
4204
|
+
});
|
|
4205
|
+
}, input.timeoutSeconds * 1e3);
|
|
4206
|
+
}
|
|
4207
|
+
setTimeout(() => {
|
|
4208
|
+
void (async () => {
|
|
4209
|
+
try {
|
|
4210
|
+
await input.hooks?.onRunning?.(sessionId);
|
|
4211
|
+
await input.hooks?.onProgress?.(sessionId, "subagent execution started");
|
|
4212
|
+
const result = await input.runTask();
|
|
4213
|
+
const session = sessions.get(sessionId);
|
|
4214
|
+
if (!session || session.terminal) {
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
if (session.cancelRequested) {
|
|
4218
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4219
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4220
|
+
});
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
if (result.isError) {
|
|
4224
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4225
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4226
|
+
code: "ASYNC_FAILED",
|
|
4227
|
+
message: result.text
|
|
4228
|
+
});
|
|
4229
|
+
});
|
|
4230
|
+
return;
|
|
4231
|
+
}
|
|
4232
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4233
|
+
await hooks?.onCompleted?.(sessionId, result);
|
|
4234
|
+
});
|
|
4235
|
+
} catch (error) {
|
|
4236
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4237
|
+
const session = sessions.get(sessionId);
|
|
4238
|
+
if (!session || session.terminal) {
|
|
4239
|
+
return;
|
|
4240
|
+
}
|
|
4241
|
+
if (session.cancelRequested) {
|
|
4242
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4243
|
+
await hooks?.onCancelled?.(sessionId, "cancel requested");
|
|
4244
|
+
});
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4248
|
+
await hooks?.onFailed?.(sessionId, {
|
|
4249
|
+
code: "ASYNC_EXCEPTION",
|
|
4250
|
+
message
|
|
4251
|
+
});
|
|
4252
|
+
});
|
|
4253
|
+
}
|
|
4254
|
+
})();
|
|
4255
|
+
}, 0);
|
|
4256
|
+
return { sessionId };
|
|
4257
|
+
},
|
|
4258
|
+
cancel: async (sessionId, reason) => {
|
|
4259
|
+
const session = sessions.get(sessionId);
|
|
4260
|
+
if (!session) {
|
|
4261
|
+
return { accepted: false };
|
|
4262
|
+
}
|
|
4263
|
+
if (session.terminal) {
|
|
4264
|
+
return { accepted: false, alreadyTerminal: true };
|
|
4265
|
+
}
|
|
4266
|
+
session.cancelRequested = true;
|
|
4267
|
+
await completeSession(sessionId, async (hooks) => {
|
|
4268
|
+
await hooks?.onCancelled?.(sessionId, reason ?? "cancel requested");
|
|
4269
|
+
});
|
|
4270
|
+
return { accepted: true };
|
|
4271
|
+
}
|
|
4272
|
+
};
|
|
4273
|
+
}
|
|
4015
4274
|
function createCloudCommand(options = {}) {
|
|
4275
|
+
const subagentOrchestrator = options.subagentOrchestrator ?? createInProcessSubagentOrchestrator();
|
|
4016
4276
|
return {
|
|
4017
|
-
name: "
|
|
4277
|
+
name: "mnemospark_cloud",
|
|
4278
|
+
nativeNames: {
|
|
4279
|
+
default: "mnemospark_cloud"
|
|
4280
|
+
},
|
|
4018
4281
|
description: "Manage mnemospark cloud storage workflow commands",
|
|
4019
4282
|
acceptsArgs: true,
|
|
4020
4283
|
requireAuth: true,
|
|
@@ -4038,6 +4301,7 @@ function createCloudCommand(options = {}) {
|
|
|
4038
4301
|
proxyQuoteOptions: options.proxyQuoteOptions,
|
|
4039
4302
|
proxyUploadOptions: options.proxyUploadOptions,
|
|
4040
4303
|
proxyUploadConfirmOptions: options.proxyUploadConfirmOptions,
|
|
4304
|
+
subagentOrchestrator,
|
|
4041
4305
|
proxyStorageOptions: options.proxyStorageOptions
|
|
4042
4306
|
});
|
|
4043
4307
|
} catch (outerError) {
|
|
@@ -4047,7 +4311,42 @@ function createCloudCommand(options = {}) {
|
|
|
4047
4311
|
}
|
|
4048
4312
|
};
|
|
4049
4313
|
}
|
|
4050
|
-
async function
|
|
4314
|
+
async function resolveFriendlyNameFromManifest(params, homeDir) {
|
|
4315
|
+
const manifestPath = join8(homeDir ?? homedir6(), ".openclaw", "mnemospark", "manifest.jsonl");
|
|
4316
|
+
let manifestRaw;
|
|
4317
|
+
try {
|
|
4318
|
+
manifestRaw = await readFile3(manifestPath, "utf-8");
|
|
4319
|
+
} catch {
|
|
4320
|
+
return { objectKey: null, matchCount: 0 };
|
|
4321
|
+
}
|
|
4322
|
+
const wallet = params.walletAddress.trim();
|
|
4323
|
+
const name = params.friendlyName.trim();
|
|
4324
|
+
const atMs = params.at ? Date.parse(params.at) : Number.NaN;
|
|
4325
|
+
const hasAt = Number.isFinite(atMs);
|
|
4326
|
+
const rows = manifestRaw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
4327
|
+
try {
|
|
4328
|
+
return JSON.parse(line);
|
|
4329
|
+
} catch {
|
|
4330
|
+
return null;
|
|
4331
|
+
}
|
|
4332
|
+
}).filter((row) => Boolean(row)).filter((row) => {
|
|
4333
|
+
if (!row.object_key || !row.friendly_name || !row.wallet_address || !row.created_at)
|
|
4334
|
+
return false;
|
|
4335
|
+
if (row.friendly_name !== name) return false;
|
|
4336
|
+
if (row.wallet_address.trim() !== wallet) return false;
|
|
4337
|
+
if (params.latest || !hasAt) return true;
|
|
4338
|
+
const createdAtMs = Date.parse(row.created_at);
|
|
4339
|
+
return Number.isFinite(createdAtMs) && createdAtMs <= atMs;
|
|
4340
|
+
}).sort((a, b) => Date.parse(b.created_at ?? "") - Date.parse(a.created_at ?? ""));
|
|
4341
|
+
if (rows.length === 0) {
|
|
4342
|
+
return { objectKey: null, matchCount: 0 };
|
|
4343
|
+
}
|
|
4344
|
+
if (!params.latest && !hasAt && rows.length > 1) {
|
|
4345
|
+
return { objectKey: null, matchCount: rows.length };
|
|
4346
|
+
}
|
|
4347
|
+
return { objectKey: rows[0].object_key ?? null, matchCount: rows.length };
|
|
4348
|
+
}
|
|
4349
|
+
async function resolveNameSelectorIfNeeded(datastore, request, selector, homeDir) {
|
|
4051
4350
|
if (!selector) {
|
|
4052
4351
|
const parsedRequest2 = parseStorageObjectRequest(request);
|
|
4053
4352
|
if (!parsedRequest2) {
|
|
@@ -4055,6 +4354,12 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
4055
4354
|
}
|
|
4056
4355
|
return { request: parsedRequest2 };
|
|
4057
4356
|
}
|
|
4357
|
+
let sqliteUnavailable = false;
|
|
4358
|
+
try {
|
|
4359
|
+
await datastore.ensureReady();
|
|
4360
|
+
} catch {
|
|
4361
|
+
sqliteUnavailable = true;
|
|
4362
|
+
}
|
|
4058
4363
|
const matches = await datastore.countFriendlyNameMatches(request.wallet_address, selector.name);
|
|
4059
4364
|
if (matches > 1 && !selector.latest && !selector.at) {
|
|
4060
4365
|
return {
|
|
@@ -4067,18 +4372,41 @@ async function resolveNameSelectorIfNeeded(datastore, request, selector) {
|
|
|
4067
4372
|
latest: selector.latest,
|
|
4068
4373
|
at: selector.at
|
|
4069
4374
|
});
|
|
4070
|
-
|
|
4375
|
+
let resolvedObjectKey = resolved?.objectKey ?? null;
|
|
4376
|
+
let degradedWarning;
|
|
4377
|
+
if (!resolvedObjectKey && sqliteUnavailable) {
|
|
4378
|
+
const manifestResolved = await resolveFriendlyNameFromManifest(
|
|
4379
|
+
{
|
|
4380
|
+
walletAddress: request.wallet_address,
|
|
4381
|
+
friendlyName: selector.name,
|
|
4382
|
+
latest: selector.latest,
|
|
4383
|
+
at: selector.at
|
|
4384
|
+
},
|
|
4385
|
+
homeDir
|
|
4386
|
+
);
|
|
4387
|
+
if (manifestResolved.matchCount > 1 && !selector.latest && !selector.at) {
|
|
4388
|
+
return {
|
|
4389
|
+
error: `Multiple objects match --name ${selector.name}. Add --latest or --at <timestamp>.`
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
resolvedObjectKey = manifestResolved.objectKey;
|
|
4393
|
+
if (resolvedObjectKey) {
|
|
4394
|
+
degradedWarning = "SQLite friendly-name index unavailable; resolved --name via manifest.jsonl fallback.";
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
if (!resolvedObjectKey) {
|
|
4071
4398
|
return { error: `No object found for --name ${selector.name}.` };
|
|
4072
4399
|
}
|
|
4073
4400
|
const parsedRequest = parseStorageObjectRequest({
|
|
4074
4401
|
...request,
|
|
4075
|
-
object_key:
|
|
4402
|
+
object_key: resolvedObjectKey
|
|
4076
4403
|
});
|
|
4077
4404
|
if (!parsedRequest) {
|
|
4078
4405
|
return { error: "Cannot resolve storage object request." };
|
|
4079
4406
|
}
|
|
4080
4407
|
return {
|
|
4081
|
-
request: parsedRequest
|
|
4408
|
+
request: parsedRequest,
|
|
4409
|
+
degradedWarning
|
|
4082
4410
|
};
|
|
4083
4411
|
}
|
|
4084
4412
|
async function emitCloudEvent(eventType, details, homeDir) {
|
|
@@ -4098,6 +4426,38 @@ async function emitCloudEventBestEffort(eventType, details, homeDir) {
|
|
|
4098
4426
|
} catch {
|
|
4099
4427
|
}
|
|
4100
4428
|
}
|
|
4429
|
+
function toOperationEventPayload(eventType, context) {
|
|
4430
|
+
return {
|
|
4431
|
+
operation_id: context.operationId,
|
|
4432
|
+
trace_id: context.traceId,
|
|
4433
|
+
event_type: eventType,
|
|
4434
|
+
status: context.status,
|
|
4435
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4436
|
+
wallet_address: context.walletAddress ?? void 0,
|
|
4437
|
+
object_id: context.objectId ?? void 0,
|
|
4438
|
+
object_key: context.objectKey ?? void 0,
|
|
4439
|
+
quote_id: context.quoteId ?? void 0,
|
|
4440
|
+
orchestrator: context.orchestrator ?? void 0,
|
|
4441
|
+
"subagent-session-id": context.subagentSessionId ?? void 0,
|
|
4442
|
+
"timeout-seconds": context.timeoutSeconds ?? void 0,
|
|
4443
|
+
"error-code": context.errorCode ?? void 0,
|
|
4444
|
+
"error-message": context.errorMessage ?? void 0,
|
|
4445
|
+
progress: context.progressMessage ?? void 0
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
async function emitOperationEvent(eventType, context, homeDir) {
|
|
4449
|
+
const payload = toOperationEventPayload(eventType, context);
|
|
4450
|
+
await Promise.all([
|
|
4451
|
+
appendJsonlEvent("events.jsonl", payload, homeDir),
|
|
4452
|
+
appendJsonlEvent("proxy-events.jsonl", payload, homeDir)
|
|
4453
|
+
]);
|
|
4454
|
+
}
|
|
4455
|
+
async function emitOperationEventBestEffort(eventType, context, homeDir) {
|
|
4456
|
+
try {
|
|
4457
|
+
await emitOperationEvent(eventType, context, homeDir);
|
|
4458
|
+
} catch {
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4101
4461
|
function buildRequestCorrelation(forcedOperationId, forcedTraceId) {
|
|
4102
4462
|
const operationId = forcedOperationId?.trim() || randomUUID3();
|
|
4103
4463
|
const traceId = forcedTraceId?.trim() || randomUUID3();
|
|
@@ -4118,6 +4478,7 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4118
4478
|
const requestStorageLs = options.requestStorageLsFn;
|
|
4119
4479
|
const requestStorageDownload = options.requestStorageDownloadFn;
|
|
4120
4480
|
const requestStorageDelete = options.requestStorageDeleteFn;
|
|
4481
|
+
const subagentOrchestrator = options.subagentOrchestrator;
|
|
4121
4482
|
if (parsed.mode === "help" || parsed.mode === "unknown") {
|
|
4122
4483
|
return {
|
|
4123
4484
|
text: CLOUD_HELP_TEXT,
|
|
@@ -4130,12 +4491,30 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4130
4491
|
isError: true
|
|
4131
4492
|
};
|
|
4132
4493
|
}
|
|
4494
|
+
if (parsed.mode === "backup-invalid") {
|
|
4495
|
+
return {
|
|
4496
|
+
text: "Cannot build storage object",
|
|
4497
|
+
isError: true
|
|
4498
|
+
};
|
|
4499
|
+
}
|
|
4500
|
+
if (parsed.mode === "backup-invalid-async") {
|
|
4501
|
+
return {
|
|
4502
|
+
text: `Cannot build storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4503
|
+
isError: true
|
|
4504
|
+
};
|
|
4505
|
+
}
|
|
4133
4506
|
if (parsed.mode === "upload-invalid") {
|
|
4134
4507
|
return {
|
|
4135
4508
|
text: `Cannot upload storage object: required arguments are ${REQUIRED_UPLOAD}.`,
|
|
4136
4509
|
isError: true
|
|
4137
4510
|
};
|
|
4138
4511
|
}
|
|
4512
|
+
if (parsed.mode === "upload-invalid-async") {
|
|
4513
|
+
return {
|
|
4514
|
+
text: `Cannot upload storage object: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4515
|
+
isError: true
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4139
4518
|
if (parsed.mode === "ls-invalid") {
|
|
4140
4519
|
return {
|
|
4141
4520
|
text: `Cannot list storage object: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4148,6 +4527,12 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4148
4527
|
isError: true
|
|
4149
4528
|
};
|
|
4150
4529
|
}
|
|
4530
|
+
if (parsed.mode === "download-invalid-async") {
|
|
4531
|
+
return {
|
|
4532
|
+
text: `Cannot download file: ${INVALID_ASYNC_FLAGS_MESSAGE}`,
|
|
4533
|
+
isError: true
|
|
4534
|
+
};
|
|
4535
|
+
}
|
|
4151
4536
|
if (parsed.mode === "delete-invalid") {
|
|
4152
4537
|
return {
|
|
4153
4538
|
text: `Cannot delete file: required arguments are ${REQUIRED_STORAGE_OBJECT}.`,
|
|
@@ -4161,70 +4546,450 @@ async function runCloudCommandHandler(ctx, options, executionContext = {}) {
|
|
|
4161
4546
|
};
|
|
4162
4547
|
}
|
|
4163
4548
|
const datastore = await createCloudDatastore(objectLogHomeDir);
|
|
4549
|
+
const terminalOperationStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled", "timed_out"]);
|
|
4550
|
+
const isTerminalOperationStatus = (status) => terminalOperationStatuses.has(status);
|
|
4551
|
+
const formatOperationStatus = (operation) => ({
|
|
4552
|
+
text: [
|
|
4553
|
+
`operation-id: ${operation.operation_id}`,
|
|
4554
|
+
`type: ${operation.type}`,
|
|
4555
|
+
`status: ${operation.status}`,
|
|
4556
|
+
`started-at: ${operation.started_at ?? "n/a"}`,
|
|
4557
|
+
`finished-at: ${operation.finished_at ?? "n/a"}`,
|
|
4558
|
+
operation.orchestrator ? `orchestrator: ${operation.orchestrator}` : null,
|
|
4559
|
+
operation.subagent_session_id ? `subagent-session-id: ${operation.subagent_session_id}` : null,
|
|
4560
|
+
operation.timeout_seconds ? `timeout-seconds: ${operation.timeout_seconds}` : null,
|
|
4561
|
+
operation.error_code ? `error-code: ${operation.error_code}` : null,
|
|
4562
|
+
operation.error_message ? `error-message: ${operation.error_message}` : null
|
|
4563
|
+
].filter((v) => Boolean(v)).join("\n"),
|
|
4564
|
+
isError: operation.status === "failed" || operation.status === "cancelled" || operation.status === "timed_out"
|
|
4565
|
+
});
|
|
4164
4566
|
if (parsed.mode === "op-status") {
|
|
4165
|
-
|
|
4567
|
+
let operation = await datastore.findOperationById(parsed.operationId);
|
|
4166
4568
|
if (!operation) {
|
|
4167
4569
|
return {
|
|
4168
4570
|
text: `Operation not found: ${parsed.operationId}`,
|
|
4169
4571
|
isError: true
|
|
4170
4572
|
};
|
|
4171
4573
|
}
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4574
|
+
if (parsed.cancel) {
|
|
4575
|
+
if (operation.orchestrator !== "subagent" || !operation.subagent_session_id) {
|
|
4576
|
+
return {
|
|
4577
|
+
text: "Cancellation is only supported for subagent-orchestrated operations.",
|
|
4578
|
+
isError: true
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
if (!isTerminalOperationStatus(operation.status)) {
|
|
4582
|
+
const traceId = operation.trace_id ?? randomUUID3();
|
|
4583
|
+
const cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4584
|
+
await datastore.upsertOperation({
|
|
4585
|
+
operation_id: operation.operation_id,
|
|
4586
|
+
type: operation.type,
|
|
4587
|
+
object_id: operation.object_id,
|
|
4588
|
+
quote_id: operation.quote_id,
|
|
4589
|
+
trace_id: traceId,
|
|
4590
|
+
orchestrator: "subagent",
|
|
4591
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4592
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4593
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4594
|
+
status: "running",
|
|
4595
|
+
error_code: null,
|
|
4596
|
+
error_message: null
|
|
4597
|
+
});
|
|
4598
|
+
await emitOperationEventBestEffort(
|
|
4599
|
+
"operation.cancel.requested",
|
|
4600
|
+
{
|
|
4601
|
+
operationId: operation.operation_id,
|
|
4602
|
+
traceId,
|
|
4603
|
+
status: "running",
|
|
4604
|
+
objectId: operation.object_id,
|
|
4605
|
+
quoteId: operation.quote_id,
|
|
4606
|
+
orchestrator: "subagent",
|
|
4607
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4608
|
+
timeoutSeconds: operation.timeout_seconds
|
|
4609
|
+
},
|
|
4610
|
+
objectLogHomeDir
|
|
4611
|
+
);
|
|
4612
|
+
const cancelResult = await subagentOrchestrator.cancel(
|
|
4613
|
+
operation.subagent_session_id,
|
|
4614
|
+
"cancel requested by op-status"
|
|
4615
|
+
);
|
|
4616
|
+
if (cancelResult.accepted || cancelResult.alreadyTerminal) {
|
|
4617
|
+
const afterCancel = await datastore.findOperationById(parsed.operationId);
|
|
4618
|
+
if (afterCancel && !isTerminalOperationStatus(afterCancel.status)) {
|
|
4619
|
+
await datastore.upsertOperation({
|
|
4620
|
+
operation_id: operation.operation_id,
|
|
4621
|
+
type: operation.type,
|
|
4622
|
+
object_id: operation.object_id,
|
|
4623
|
+
quote_id: operation.quote_id,
|
|
4624
|
+
trace_id: traceId,
|
|
4625
|
+
orchestrator: "subagent",
|
|
4626
|
+
subagent_session_id: operation.subagent_session_id,
|
|
4627
|
+
timeout_seconds: operation.timeout_seconds,
|
|
4628
|
+
cancel_requested_at: cancelRequestedAt,
|
|
4629
|
+
status: "cancelled",
|
|
4630
|
+
error_code: "ASYNC_CANCELLED",
|
|
4631
|
+
error_message: "Operation cancelled by user request."
|
|
4632
|
+
});
|
|
4633
|
+
await emitOperationEventBestEffort(
|
|
4634
|
+
"operation.cancelled",
|
|
4635
|
+
{
|
|
4636
|
+
operationId: operation.operation_id,
|
|
4637
|
+
traceId,
|
|
4638
|
+
status: "cancelled",
|
|
4639
|
+
objectId: operation.object_id,
|
|
4640
|
+
quoteId: operation.quote_id,
|
|
4641
|
+
orchestrator: "subagent",
|
|
4642
|
+
subagentSessionId: operation.subagent_session_id,
|
|
4643
|
+
timeoutSeconds: operation.timeout_seconds,
|
|
4644
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4645
|
+
errorMessage: "Operation cancelled by user request."
|
|
4646
|
+
},
|
|
4647
|
+
objectLogHomeDir
|
|
4648
|
+
);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
operation = await datastore.findOperationById(parsed.operationId);
|
|
4653
|
+
if (!operation) {
|
|
4654
|
+
return {
|
|
4655
|
+
text: `Operation not found: ${parsed.operationId}`,
|
|
4656
|
+
isError: true
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
return formatOperationStatus(operation);
|
|
4184
4661
|
}
|
|
4185
|
-
if ((parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4662
|
+
if ((parsed.mode === "backup" || parsed.mode === "upload" || parsed.mode === "download") && parsed.async) {
|
|
4186
4663
|
const asyncCorrelation = buildRequestCorrelation();
|
|
4187
4664
|
const operationId = asyncCorrelation.operationId;
|
|
4188
4665
|
const opType = parsed.mode;
|
|
4189
4666
|
const opObject = parsed.mode === "upload" ? parsed.uploadRequest.object_id : null;
|
|
4190
4667
|
const opQuote = parsed.mode === "upload" ? parsed.uploadRequest.quote_id : null;
|
|
4668
|
+
const orchestratorMode = parsed.orchestrator ?? "inline";
|
|
4669
|
+
const timeoutSeconds = orchestratorMode === "subagent" ? parsed.timeoutSeconds ?? null : null;
|
|
4670
|
+
const eventContextBase = {
|
|
4671
|
+
operationId,
|
|
4672
|
+
traceId: asyncCorrelation.traceId,
|
|
4673
|
+
walletAddress: parsed.mode === "upload" ? parsed.uploadRequest.wallet_address : parsed.mode === "download" ? parsed.storageObjectRequest.wallet_address : null,
|
|
4674
|
+
objectId: opObject,
|
|
4675
|
+
objectKey: parsed.mode === "download" ? parsed.storageObjectRequest.object_key ?? null : null,
|
|
4676
|
+
quoteId: opQuote,
|
|
4677
|
+
orchestrator: orchestratorMode,
|
|
4678
|
+
timeoutSeconds
|
|
4679
|
+
};
|
|
4191
4680
|
await datastore.upsertOperation({
|
|
4192
4681
|
operation_id: operationId,
|
|
4193
4682
|
type: opType,
|
|
4194
4683
|
object_id: opObject,
|
|
4195
4684
|
quote_id: opQuote,
|
|
4685
|
+
trace_id: asyncCorrelation.traceId,
|
|
4686
|
+
orchestrator: orchestratorMode,
|
|
4687
|
+
timeout_seconds: timeoutSeconds,
|
|
4196
4688
|
status: "started",
|
|
4197
4689
|
error_code: null,
|
|
4198
4690
|
error_message: null
|
|
4199
4691
|
});
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4692
|
+
await emitOperationEventBestEffort(
|
|
4693
|
+
"operation.dispatched",
|
|
4694
|
+
{ ...eventContextBase, status: "started" },
|
|
4695
|
+
objectLogHomeDir
|
|
4696
|
+
);
|
|
4697
|
+
const syncArgs = stripAsyncControlFlags(ctx.args);
|
|
4698
|
+
if (orchestratorMode === "subagent") {
|
|
4699
|
+
const subagentTask = {
|
|
4700
|
+
schema: "mnemospark.subagent-task.v1",
|
|
4701
|
+
operationId,
|
|
4702
|
+
traceId: asyncCorrelation.traceId,
|
|
4703
|
+
command: parsed.mode,
|
|
4704
|
+
args: syncArgs,
|
|
4705
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4706
|
+
requestedBy: {
|
|
4707
|
+
pluginCommand: "mnemospark_cloud",
|
|
4708
|
+
chatId: ctx.channel,
|
|
4709
|
+
senderId: ctx.senderId
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
try {
|
|
4713
|
+
const dispatchResult = await subagentOrchestrator.dispatch({
|
|
4714
|
+
task: subagentTask,
|
|
4715
|
+
timeoutSeconds: parsed.timeoutSeconds,
|
|
4716
|
+
runTask: async () => runCloudCommandHandler(
|
|
4717
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4718
|
+
options,
|
|
4719
|
+
{
|
|
4720
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4721
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4722
|
+
}
|
|
4723
|
+
),
|
|
4724
|
+
hooks: {
|
|
4725
|
+
onRunning: async (sessionId) => {
|
|
4726
|
+
await datastore.upsertOperation({
|
|
4727
|
+
operation_id: operationId,
|
|
4728
|
+
type: opType,
|
|
4729
|
+
object_id: opObject,
|
|
4730
|
+
quote_id: opQuote,
|
|
4731
|
+
trace_id: asyncCorrelation.traceId,
|
|
4732
|
+
orchestrator: "subagent",
|
|
4733
|
+
subagent_session_id: sessionId,
|
|
4734
|
+
timeout_seconds: timeoutSeconds,
|
|
4735
|
+
status: "running",
|
|
4736
|
+
error_code: null,
|
|
4737
|
+
error_message: null
|
|
4738
|
+
});
|
|
4739
|
+
await emitOperationEventBestEffort(
|
|
4740
|
+
"operation.progress",
|
|
4741
|
+
{
|
|
4742
|
+
...eventContextBase,
|
|
4743
|
+
status: "running",
|
|
4744
|
+
subagentSessionId: sessionId,
|
|
4745
|
+
progressMessage: "subagent running"
|
|
4746
|
+
},
|
|
4747
|
+
objectLogHomeDir
|
|
4748
|
+
);
|
|
4749
|
+
},
|
|
4750
|
+
onProgress: async (sessionId, message) => {
|
|
4751
|
+
await emitOperationEventBestEffort(
|
|
4752
|
+
"operation.progress",
|
|
4753
|
+
{
|
|
4754
|
+
...eventContextBase,
|
|
4755
|
+
status: "running",
|
|
4756
|
+
subagentSessionId: sessionId,
|
|
4757
|
+
progressMessage: message
|
|
4758
|
+
},
|
|
4759
|
+
objectLogHomeDir
|
|
4760
|
+
);
|
|
4761
|
+
},
|
|
4762
|
+
onCompleted: async (sessionId) => {
|
|
4763
|
+
await datastore.upsertOperation({
|
|
4764
|
+
operation_id: operationId,
|
|
4765
|
+
type: opType,
|
|
4766
|
+
object_id: opObject,
|
|
4767
|
+
quote_id: opQuote,
|
|
4768
|
+
trace_id: asyncCorrelation.traceId,
|
|
4769
|
+
orchestrator: "subagent",
|
|
4770
|
+
subagent_session_id: sessionId,
|
|
4771
|
+
timeout_seconds: timeoutSeconds,
|
|
4772
|
+
status: "succeeded",
|
|
4773
|
+
error_code: null,
|
|
4774
|
+
error_message: null
|
|
4775
|
+
});
|
|
4776
|
+
await emitOperationEventBestEffort(
|
|
4777
|
+
"operation.completed",
|
|
4778
|
+
{
|
|
4779
|
+
...eventContextBase,
|
|
4780
|
+
status: "succeeded",
|
|
4781
|
+
subagentSessionId: sessionId
|
|
4782
|
+
},
|
|
4783
|
+
objectLogHomeDir
|
|
4784
|
+
);
|
|
4785
|
+
},
|
|
4786
|
+
onFailed: async (sessionId, details) => {
|
|
4787
|
+
await datastore.upsertOperation({
|
|
4788
|
+
operation_id: operationId,
|
|
4789
|
+
type: opType,
|
|
4790
|
+
object_id: opObject,
|
|
4791
|
+
quote_id: opQuote,
|
|
4792
|
+
trace_id: asyncCorrelation.traceId,
|
|
4793
|
+
orchestrator: "subagent",
|
|
4794
|
+
subagent_session_id: sessionId,
|
|
4795
|
+
timeout_seconds: timeoutSeconds,
|
|
4796
|
+
status: "failed",
|
|
4797
|
+
error_code: details.code,
|
|
4798
|
+
error_message: details.message
|
|
4799
|
+
});
|
|
4800
|
+
await emitOperationEventBestEffort(
|
|
4801
|
+
"operation.completed",
|
|
4802
|
+
{
|
|
4803
|
+
...eventContextBase,
|
|
4804
|
+
status: "failed",
|
|
4805
|
+
subagentSessionId: sessionId,
|
|
4806
|
+
errorCode: details.code,
|
|
4807
|
+
errorMessage: details.message
|
|
4808
|
+
},
|
|
4809
|
+
objectLogHomeDir
|
|
4810
|
+
);
|
|
4811
|
+
},
|
|
4812
|
+
onCancelled: async (sessionId, reason) => {
|
|
4813
|
+
await datastore.upsertOperation({
|
|
4814
|
+
operation_id: operationId,
|
|
4815
|
+
type: opType,
|
|
4816
|
+
object_id: opObject,
|
|
4817
|
+
quote_id: opQuote,
|
|
4818
|
+
trace_id: asyncCorrelation.traceId,
|
|
4819
|
+
orchestrator: "subagent",
|
|
4820
|
+
subagent_session_id: sessionId,
|
|
4821
|
+
timeout_seconds: timeoutSeconds,
|
|
4822
|
+
cancel_requested_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4823
|
+
status: "cancelled",
|
|
4824
|
+
error_code: "ASYNC_CANCELLED",
|
|
4825
|
+
error_message: reason ?? "Operation cancelled."
|
|
4826
|
+
});
|
|
4827
|
+
await emitOperationEventBestEffort(
|
|
4828
|
+
"operation.cancelled",
|
|
4829
|
+
{
|
|
4830
|
+
...eventContextBase,
|
|
4831
|
+
status: "cancelled",
|
|
4832
|
+
subagentSessionId: sessionId,
|
|
4833
|
+
errorCode: "ASYNC_CANCELLED",
|
|
4834
|
+
errorMessage: reason ?? "Operation cancelled."
|
|
4835
|
+
},
|
|
4836
|
+
objectLogHomeDir
|
|
4837
|
+
);
|
|
4838
|
+
},
|
|
4839
|
+
onTimedOut: async (sessionId) => {
|
|
4840
|
+
await datastore.upsertOperation({
|
|
4841
|
+
operation_id: operationId,
|
|
4842
|
+
type: opType,
|
|
4843
|
+
object_id: opObject,
|
|
4844
|
+
quote_id: opQuote,
|
|
4845
|
+
trace_id: asyncCorrelation.traceId,
|
|
4846
|
+
orchestrator: "subagent",
|
|
4847
|
+
subagent_session_id: sessionId,
|
|
4848
|
+
timeout_seconds: timeoutSeconds,
|
|
4849
|
+
status: "timed_out",
|
|
4850
|
+
error_code: "ASYNC_TIMEOUT",
|
|
4851
|
+
error_message: "Operation timed out."
|
|
4852
|
+
});
|
|
4853
|
+
await emitOperationEventBestEffort(
|
|
4854
|
+
"operation.timed_out",
|
|
4855
|
+
{
|
|
4856
|
+
...eventContextBase,
|
|
4857
|
+
status: "timed_out",
|
|
4858
|
+
subagentSessionId: sessionId,
|
|
4859
|
+
errorCode: "ASYNC_TIMEOUT",
|
|
4860
|
+
errorMessage: "Operation timed out."
|
|
4861
|
+
},
|
|
4862
|
+
objectLogHomeDir
|
|
4863
|
+
);
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
});
|
|
4867
|
+
const operationAfterDispatch = await datastore.findOperationById(operationId);
|
|
4868
|
+
if (operationAfterDispatch?.subagent_session_id !== dispatchResult.sessionId) {
|
|
4869
|
+
await datastore.upsertOperation({
|
|
4870
|
+
operation_id: operationId,
|
|
4871
|
+
type: opType,
|
|
4872
|
+
object_id: opObject,
|
|
4873
|
+
quote_id: opQuote,
|
|
4874
|
+
trace_id: asyncCorrelation.traceId,
|
|
4875
|
+
orchestrator: "subagent",
|
|
4876
|
+
subagent_session_id: dispatchResult.sessionId,
|
|
4877
|
+
timeout_seconds: timeoutSeconds,
|
|
4878
|
+
status: operationAfterDispatch?.status ?? "started",
|
|
4879
|
+
error_code: operationAfterDispatch?.error_code ?? null,
|
|
4880
|
+
error_message: operationAfterDispatch?.error_message ?? null
|
|
4881
|
+
});
|
|
4882
|
+
}
|
|
4883
|
+
return {
|
|
4884
|
+
text: [
|
|
4885
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4886
|
+
`orchestrator: subagent`,
|
|
4887
|
+
`subagent-session-id: ${dispatchResult.sessionId}`,
|
|
4888
|
+
timeoutSeconds ? `timeout-seconds: ${timeoutSeconds}` : null,
|
|
4889
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4890
|
+
].filter((line) => Boolean(line)).join("\n")
|
|
4891
|
+
};
|
|
4892
|
+
} catch (dispatchError) {
|
|
4893
|
+
const dispatchMessage = dispatchError instanceof Error ? dispatchError.message : String(dispatchError);
|
|
4894
|
+
await datastore.upsertOperation({
|
|
4895
|
+
operation_id: operationId,
|
|
4896
|
+
type: opType,
|
|
4897
|
+
object_id: opObject,
|
|
4898
|
+
quote_id: opQuote,
|
|
4899
|
+
trace_id: asyncCorrelation.traceId,
|
|
4900
|
+
orchestrator: "subagent",
|
|
4901
|
+
timeout_seconds: timeoutSeconds,
|
|
4902
|
+
status: "failed",
|
|
4903
|
+
error_code: "ASYNC_DISPATCH_FAILED",
|
|
4904
|
+
error_message: dispatchMessage
|
|
4905
|
+
});
|
|
4906
|
+
await emitOperationEventBestEffort(
|
|
4907
|
+
"operation.completed",
|
|
4908
|
+
{
|
|
4909
|
+
...eventContextBase,
|
|
4910
|
+
status: "failed",
|
|
4911
|
+
errorCode: "ASYNC_DISPATCH_FAILED",
|
|
4912
|
+
errorMessage: dispatchMessage
|
|
4913
|
+
},
|
|
4914
|
+
objectLogHomeDir
|
|
4915
|
+
);
|
|
4916
|
+
return {
|
|
4917
|
+
text: `Cannot dispatch subagent operation: ${dispatchMessage}
|
|
4918
|
+
operation-id: ${operationId}`,
|
|
4919
|
+
isError: true
|
|
4920
|
+
};
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
await datastore.upsertOperation({
|
|
4924
|
+
operation_id: operationId,
|
|
4925
|
+
type: opType,
|
|
4926
|
+
object_id: opObject,
|
|
4927
|
+
quote_id: opQuote,
|
|
4928
|
+
trace_id: asyncCorrelation.traceId,
|
|
4929
|
+
orchestrator: "inline",
|
|
4930
|
+
status: "running",
|
|
4931
|
+
error_code: null,
|
|
4932
|
+
error_message: null
|
|
4933
|
+
});
|
|
4934
|
+
void runCloudCommandHandler(
|
|
4935
|
+
{ args: syncArgs, channel: ctx.channel, senderId: ctx.senderId },
|
|
4936
|
+
options,
|
|
4937
|
+
{
|
|
4938
|
+
forcedOperationId: asyncCorrelation.operationId,
|
|
4939
|
+
forcedTraceId: asyncCorrelation.traceId
|
|
4940
|
+
}
|
|
4941
|
+
).then(async (result) => {
|
|
4205
4942
|
await datastore.upsertOperation({
|
|
4206
4943
|
operation_id: operationId,
|
|
4207
4944
|
type: opType,
|
|
4208
4945
|
object_id: opObject,
|
|
4209
4946
|
quote_id: opQuote,
|
|
4947
|
+
trace_id: asyncCorrelation.traceId,
|
|
4948
|
+
orchestrator: "inline",
|
|
4210
4949
|
status: result.isError ? "failed" : "succeeded",
|
|
4211
4950
|
error_code: result.isError ? "ASYNC_FAILED" : null,
|
|
4212
4951
|
error_message: result.isError ? result.text : null
|
|
4213
4952
|
});
|
|
4953
|
+
await emitOperationEventBestEffort(
|
|
4954
|
+
"operation.completed",
|
|
4955
|
+
{
|
|
4956
|
+
...eventContextBase,
|
|
4957
|
+
status: result.isError ? "failed" : "succeeded",
|
|
4958
|
+
errorCode: result.isError ? "ASYNC_FAILED" : null,
|
|
4959
|
+
errorMessage: result.isError ? result.text : null
|
|
4960
|
+
},
|
|
4961
|
+
objectLogHomeDir
|
|
4962
|
+
);
|
|
4214
4963
|
}).catch(async (err) => {
|
|
4964
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
4215
4965
|
await datastore.upsertOperation({
|
|
4216
4966
|
operation_id: operationId,
|
|
4217
4967
|
type: opType,
|
|
4218
4968
|
object_id: opObject,
|
|
4219
4969
|
quote_id: opQuote,
|
|
4970
|
+
trace_id: asyncCorrelation.traceId,
|
|
4971
|
+
orchestrator: "inline",
|
|
4220
4972
|
status: "failed",
|
|
4221
4973
|
error_code: "ASYNC_EXCEPTION",
|
|
4222
|
-
error_message:
|
|
4974
|
+
error_message: errorMessage
|
|
4223
4975
|
});
|
|
4976
|
+
await emitOperationEventBestEffort(
|
|
4977
|
+
"operation.completed",
|
|
4978
|
+
{
|
|
4979
|
+
...eventContextBase,
|
|
4980
|
+
status: "failed",
|
|
4981
|
+
errorCode: "ASYNC_EXCEPTION",
|
|
4982
|
+
errorMessage
|
|
4983
|
+
},
|
|
4984
|
+
objectLogHomeDir
|
|
4985
|
+
);
|
|
4224
4986
|
});
|
|
4225
4987
|
return {
|
|
4226
|
-
text:
|
|
4227
|
-
|
|
4988
|
+
text: [
|
|
4989
|
+
`Operation started in background. operation-id: ${operationId}`,
|
|
4990
|
+
`orchestrator: inline`,
|
|
4991
|
+
`Use /mnemospark_cloud op-status --operation-id ${operationId}`
|
|
4992
|
+
].join("\n")
|
|
4228
4993
|
};
|
|
4229
4994
|
}
|
|
4230
4995
|
if (parsed.mode === "backup") {
|
|
@@ -4351,7 +5116,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4351
5116
|
const loggedQuote = await datastore.findQuoteById(parsed.uploadRequest.quote_id) ?? await findLoggedPriceStorageQuote(parsed.uploadRequest.quote_id, objectLogHomeDir);
|
|
4352
5117
|
if (!loggedQuote) {
|
|
4353
5118
|
return {
|
|
4354
|
-
text: "Cannot upload storage object: quote-id not found in object.log. Run /
|
|
5119
|
+
text: "Cannot upload storage object: quote-id not found in object.log. Run /mnemospark_cloud price-storage first.",
|
|
4355
5120
|
isError: true
|
|
4356
5121
|
};
|
|
4357
5122
|
}
|
|
@@ -4370,7 +5135,7 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4370
5135
|
archiveStats = await stat2(archivePath);
|
|
4371
5136
|
} catch {
|
|
4372
5137
|
return {
|
|
4373
|
-
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /
|
|
5138
|
+
text: `Cannot upload storage object: local archive not found at ${archivePath}. Run /mnemospark_cloud backup first.`,
|
|
4374
5139
|
isError: true
|
|
4375
5140
|
};
|
|
4376
5141
|
}
|
|
@@ -4503,18 +5268,50 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4503
5268
|
status: "active"
|
|
4504
5269
|
});
|
|
4505
5270
|
if (parsed.friendlyName?.trim()) {
|
|
5271
|
+
const normalizedFriendlyName = parsed.friendlyName.trim();
|
|
4506
5272
|
await datastore.upsertFriendlyName({
|
|
4507
|
-
friendly_name:
|
|
5273
|
+
friendly_name: normalizedFriendlyName,
|
|
4508
5274
|
object_id: finalizedUploadResponse.object_id,
|
|
4509
5275
|
object_key: finalizedUploadResponse.object_key,
|
|
4510
5276
|
quote_id: finalizedUploadResponse.quote_id,
|
|
4511
5277
|
wallet_address: finalizedUploadResponse.addr
|
|
4512
5278
|
});
|
|
5279
|
+
let friendlyNameVerified = false;
|
|
5280
|
+
try {
|
|
5281
|
+
const readBack = await datastore.resolveFriendlyName({
|
|
5282
|
+
walletAddress: finalizedUploadResponse.addr,
|
|
5283
|
+
friendlyName: normalizedFriendlyName,
|
|
5284
|
+
latest: true
|
|
5285
|
+
});
|
|
5286
|
+
friendlyNameVerified = Boolean(readBack?.objectKey) && readBack?.objectKey === finalizedUploadResponse.object_key;
|
|
5287
|
+
} catch {
|
|
5288
|
+
friendlyNameVerified = false;
|
|
5289
|
+
}
|
|
5290
|
+
if (!friendlyNameVerified) {
|
|
5291
|
+
const warning = "SQLite friendly-name write verification failed; manifest fallback may be required for --name lookups.";
|
|
5292
|
+
await emitCloudEventBestEffort(
|
|
5293
|
+
"friendly_name.write_verification_failed",
|
|
5294
|
+
{
|
|
5295
|
+
operation_id: uploadCorrelation.operationId,
|
|
5296
|
+
trace_id: uploadCorrelation.traceId,
|
|
5297
|
+
wallet_address: finalizedUploadResponse.addr,
|
|
5298
|
+
object_id: finalizedUploadResponse.object_id,
|
|
5299
|
+
object_key: finalizedUploadResponse.object_key,
|
|
5300
|
+
quote_id: finalizedUploadResponse.quote_id,
|
|
5301
|
+
friendly_name: normalizedFriendlyName,
|
|
5302
|
+
warning
|
|
5303
|
+
},
|
|
5304
|
+
objectLogHomeDir
|
|
5305
|
+
);
|
|
5306
|
+
if (process.env.MNEMOSPARK_SQLITE_STRICT === "1") {
|
|
5307
|
+
throw new Error(warning);
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
4513
5310
|
try {
|
|
4514
5311
|
await appendJsonlEvent(
|
|
4515
5312
|
"manifest.jsonl",
|
|
4516
5313
|
{
|
|
4517
|
-
friendly_name:
|
|
5314
|
+
friendly_name: normalizedFriendlyName,
|
|
4518
5315
|
object_id: finalizedUploadResponse.object_id,
|
|
4519
5316
|
object_key: finalizedUploadResponse.object_key,
|
|
4520
5317
|
quote_id: finalizedUploadResponse.quote_id,
|
|
@@ -4567,12 +5364,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4567
5364
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4568
5365
|
datastore,
|
|
4569
5366
|
parsed.storageObjectRequest,
|
|
4570
|
-
parsed.nameSelector
|
|
5367
|
+
parsed.nameSelector,
|
|
5368
|
+
objectLogHomeDir
|
|
4571
5369
|
);
|
|
4572
5370
|
if (resolved.error || !resolved.request) {
|
|
4573
5371
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4574
5372
|
}
|
|
4575
5373
|
const resolvedRequest = resolved.request;
|
|
5374
|
+
if (resolved.degradedWarning) {
|
|
5375
|
+
await emitCloudEventBestEffort(
|
|
5376
|
+
"name_resolution.degraded",
|
|
5377
|
+
{
|
|
5378
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5379
|
+
object_key: resolvedRequest.object_key,
|
|
5380
|
+
warning: resolved.degradedWarning
|
|
5381
|
+
},
|
|
5382
|
+
objectLogHomeDir
|
|
5383
|
+
);
|
|
5384
|
+
}
|
|
4576
5385
|
const correlation = buildRequestCorrelation();
|
|
4577
5386
|
const operationId = correlation.operationId;
|
|
4578
5387
|
const knownObject = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
@@ -4614,8 +5423,10 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4614
5423
|
},
|
|
4615
5424
|
objectLogHomeDir
|
|
4616
5425
|
);
|
|
5426
|
+
const lsText = formatStorageLsUserMessage(lsResult, resolvedRequest.object_key);
|
|
4617
5427
|
return {
|
|
4618
|
-
text:
|
|
5428
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5429
|
+
${lsText}` : lsText
|
|
4619
5430
|
};
|
|
4620
5431
|
} catch {
|
|
4621
5432
|
await datastore.upsertOperation({
|
|
@@ -4648,12 +5459,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4648
5459
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4649
5460
|
datastore,
|
|
4650
5461
|
parsed.storageObjectRequest,
|
|
4651
|
-
parsed.nameSelector
|
|
5462
|
+
parsed.nameSelector,
|
|
5463
|
+
objectLogHomeDir
|
|
4652
5464
|
);
|
|
4653
5465
|
if (resolved.error || !resolved.request) {
|
|
4654
5466
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4655
5467
|
}
|
|
4656
5468
|
const resolvedRequest = resolved.request;
|
|
5469
|
+
if (resolved.degradedWarning) {
|
|
5470
|
+
await emitCloudEventBestEffort(
|
|
5471
|
+
"name_resolution.degraded",
|
|
5472
|
+
{
|
|
5473
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5474
|
+
object_key: resolvedRequest.object_key,
|
|
5475
|
+
warning: resolved.degradedWarning
|
|
5476
|
+
},
|
|
5477
|
+
objectLogHomeDir
|
|
5478
|
+
);
|
|
5479
|
+
}
|
|
4657
5480
|
const correlation = buildRequestCorrelation(
|
|
4658
5481
|
executionContext.forcedOperationId,
|
|
4659
5482
|
executionContext.forcedTraceId
|
|
@@ -4698,8 +5521,10 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4698
5521
|
},
|
|
4699
5522
|
objectLogHomeDir
|
|
4700
5523
|
);
|
|
5524
|
+
const downloadText = `File ${resolvedRequest.object_key} downloaded to ${downloadResult.file_path}`;
|
|
4701
5525
|
return {
|
|
4702
|
-
text:
|
|
5526
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5527
|
+
${downloadText}` : downloadText
|
|
4703
5528
|
};
|
|
4704
5529
|
} catch {
|
|
4705
5530
|
await datastore.upsertOperation({
|
|
@@ -4732,12 +5557,24 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4732
5557
|
const resolved = await resolveNameSelectorIfNeeded(
|
|
4733
5558
|
datastore,
|
|
4734
5559
|
parsed.storageObjectRequest,
|
|
4735
|
-
parsed.nameSelector
|
|
5560
|
+
parsed.nameSelector,
|
|
5561
|
+
objectLogHomeDir
|
|
4736
5562
|
);
|
|
4737
5563
|
if (resolved.error || !resolved.request) {
|
|
4738
5564
|
return { text: resolved.error ?? "Cannot resolve storage object request.", isError: true };
|
|
4739
5565
|
}
|
|
4740
5566
|
const resolvedRequest = resolved.request;
|
|
5567
|
+
if (resolved.degradedWarning) {
|
|
5568
|
+
await emitCloudEventBestEffort(
|
|
5569
|
+
"name_resolution.degraded",
|
|
5570
|
+
{
|
|
5571
|
+
wallet_address: resolvedRequest.wallet_address,
|
|
5572
|
+
object_key: resolvedRequest.object_key,
|
|
5573
|
+
warning: resolved.degradedWarning
|
|
5574
|
+
},
|
|
5575
|
+
objectLogHomeDir
|
|
5576
|
+
);
|
|
5577
|
+
}
|
|
4741
5578
|
const correlation = buildRequestCorrelation();
|
|
4742
5579
|
const operationId = correlation.operationId;
|
|
4743
5580
|
const existingObjectByKey = await datastore.findObjectByObjectKey(resolvedRequest.object_key);
|
|
@@ -4819,12 +5656,14 @@ Use /mnemospark-cloud op-status --operation-id ${operationId}`
|
|
|
4819
5656
|
},
|
|
4820
5657
|
objectLogHomeDir
|
|
4821
5658
|
);
|
|
5659
|
+
const deleteText = formatStorageDeleteUserMessage(
|
|
5660
|
+
resolvedRequest.object_key,
|
|
5661
|
+
cronEntry?.cronId ?? null,
|
|
5662
|
+
cronDeleted
|
|
5663
|
+
);
|
|
4822
5664
|
return {
|
|
4823
|
-
text:
|
|
4824
|
-
|
|
4825
|
-
cronEntry?.cronId ?? null,
|
|
4826
|
-
cronDeleted
|
|
4827
|
-
)
|
|
5665
|
+
text: resolved.degradedWarning ? `${resolved.degradedWarning}
|
|
5666
|
+
${deleteText}` : deleteText
|
|
4828
5667
|
};
|
|
4829
5668
|
}
|
|
4830
5669
|
return {
|
|
@@ -4942,7 +5781,10 @@ async function startProxyInBackground(api) {
|
|
|
4942
5781
|
}
|
|
4943
5782
|
async function createWalletCommand() {
|
|
4944
5783
|
return {
|
|
4945
|
-
name: "
|
|
5784
|
+
name: "mnemospark_wallet",
|
|
5785
|
+
nativeNames: {
|
|
5786
|
+
default: "mnemospark_wallet"
|
|
5787
|
+
},
|
|
4946
5788
|
description: "Show mnemospark wallet info or export private key for backup",
|
|
4947
5789
|
acceptsArgs: true,
|
|
4948
5790
|
requireAuth: true,
|
|
@@ -5006,8 +5848,8 @@ async function createWalletCommand() {
|
|
|
5006
5848
|
`**Key File:** \`${WALLET_FILE}\``,
|
|
5007
5849
|
"",
|
|
5008
5850
|
"**Commands:**",
|
|
5009
|
-
"\u2022 `/
|
|
5010
|
-
"\u2022 `/
|
|
5851
|
+
"\u2022 `/mnemospark_wallet` - Show this status",
|
|
5852
|
+
"\u2022 `/mnemospark_wallet export` - Export private key for backup",
|
|
5011
5853
|
"",
|
|
5012
5854
|
`**Fund with USDC on Base:** https://basescan.org/address/${address}`
|
|
5013
5855
|
].join("\n")
|
|
@@ -5033,14 +5875,14 @@ var plugin = {
|
|
|
5033
5875
|
api.registerCommand(walletCommand);
|
|
5034
5876
|
}).catch((err) => {
|
|
5035
5877
|
api.logger.warn(
|
|
5036
|
-
`Failed to register /
|
|
5878
|
+
`Failed to register /mnemospark_wallet command: ${err instanceof Error ? err.message : String(err)}`
|
|
5037
5879
|
);
|
|
5038
5880
|
});
|
|
5039
5881
|
try {
|
|
5040
5882
|
api.registerCommand(createCloudCommand());
|
|
5041
5883
|
} catch (err) {
|
|
5042
5884
|
api.logger.warn(
|
|
5043
|
-
`Failed to register /
|
|
5885
|
+
`Failed to register /mnemospark_cloud command: ${err instanceof Error ? err.message : String(err)}`
|
|
5044
5886
|
);
|
|
5045
5887
|
}
|
|
5046
5888
|
api.registerService({
|