struere 0.14.1 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/struere.js +495 -69
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/keys.d.ts +3 -0
- package/dist/cli/commands/keys.d.ts.map +1 -0
- package/dist/cli/commands/sync.d.ts +6 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/triggers.d.ts.map +1 -1
- package/dist/cli/index.js +495 -69
- package/dist/cli/utils/apiKeys.d.ts +31 -0
- package/dist/cli/utils/apiKeys.d.ts.map +1 -0
- package/dist/cli/utils/convex.d.ts +7 -0
- package/dist/cli/utils/convex.d.ts.map +1 -1
- package/dist/cli/utils/extractor.d.ts +11 -1
- package/dist/cli/utils/extractor.d.ts.map +1 -1
- package/dist/cli/utils/logs.d.ts +4 -3
- package/dist/cli/utils/logs.d.ts.map +1 -1
- package/dist/cli/utils/threads.d.ts +1 -0
- package/dist/cli/utils/threads.d.ts.map +1 -1
- package/dist/cli/utils/triggers.d.ts +12 -10
- package/dist/cli/utils/triggers.d.ts.map +1 -1
- package/dist/client/chatClient.d.ts +8 -0
- package/dist/client/chatClient.d.ts.map +1 -0
- package/dist/client/dataClient.d.ts +21 -0
- package/dist/client/dataClient.d.ts.map +1 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +245 -0
- package/dist/client/transport.d.ts +35 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/types.d.ts +130 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
package/dist/bin/struere.js
CHANGED
|
@@ -680,7 +680,8 @@ async function _fireTrigger(options) {
|
|
|
680
680
|
slug: options.slug,
|
|
681
681
|
environment: options.environment,
|
|
682
682
|
entityId: options.entityId,
|
|
683
|
-
data: options.data
|
|
683
|
+
data: options.data,
|
|
684
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
684
685
|
}
|
|
685
686
|
}),
|
|
686
687
|
signal: AbortSignal.timeout(60000)
|
|
@@ -3226,6 +3227,42 @@ function validateResources(payload, resources) {
|
|
|
3226
3227
|
}
|
|
3227
3228
|
|
|
3228
3229
|
// src/cli/commands/sync.ts
|
|
3230
|
+
class SyncValidationFailureError extends Error {
|
|
3231
|
+
validationErrors;
|
|
3232
|
+
constructor(validationErrors) {
|
|
3233
|
+
super("Server-side validation failed for one or more resources");
|
|
3234
|
+
this.name = "SyncValidationFailureError";
|
|
3235
|
+
this.validationErrors = validationErrors;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
function renderValidationErrors(errors) {
|
|
3239
|
+
console.log();
|
|
3240
|
+
console.log(chalk6.red.bold("Validation failed \u2014 no changes were applied."));
|
|
3241
|
+
console.log();
|
|
3242
|
+
const labels = {
|
|
3243
|
+
agents: "agent",
|
|
3244
|
+
entityTypes: "entity type",
|
|
3245
|
+
roles: "role",
|
|
3246
|
+
evalSuites: "eval suite",
|
|
3247
|
+
triggers: "trigger",
|
|
3248
|
+
fixtures: "fixture",
|
|
3249
|
+
routers: "router",
|
|
3250
|
+
tools: "tool"
|
|
3251
|
+
};
|
|
3252
|
+
const order = ["agents", "entityTypes", "roles", "evalSuites", "triggers", "fixtures", "routers", "tools"];
|
|
3253
|
+
for (const key of order) {
|
|
3254
|
+
const items = errors[key];
|
|
3255
|
+
if (!items || items.length === 0)
|
|
3256
|
+
continue;
|
|
3257
|
+
const label = labels[key] || key;
|
|
3258
|
+
console.log(chalk6.red.bold(` ${key}:`), chalk6.red(`${items.length} invalid ${label}${items.length === 1 ? "" : "s"}`));
|
|
3259
|
+
for (const item of items) {
|
|
3260
|
+
const id = item.slug || item.name || "(unidentified)";
|
|
3261
|
+
console.log(chalk6.red(` - ${id}: ${item.error}`));
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
console.log();
|
|
3265
|
+
}
|
|
3229
3266
|
async function performDevSync(cwd, organizationId) {
|
|
3230
3267
|
generateTypeDeclarations(cwd);
|
|
3231
3268
|
const resources = await loadAllResources(cwd);
|
|
@@ -3256,6 +3293,8 @@ ${resources.errors.join(`
|
|
|
3256
3293
|
environment: "development"
|
|
3257
3294
|
});
|
|
3258
3295
|
if (!devResult.success) {
|
|
3296
|
+
if (devResult.validationErrors)
|
|
3297
|
+
throw new SyncValidationFailureError(devResult.validationErrors);
|
|
3259
3298
|
throw new Error(devResult.error || "Dev sync failed");
|
|
3260
3299
|
}
|
|
3261
3300
|
const hasEvalContent = payload.evalSuites && payload.evalSuites.length > 0 || payload.fixtures && payload.fixtures.length > 0;
|
|
@@ -3271,6 +3310,8 @@ ${resources.errors.join(`
|
|
|
3271
3310
|
environment: "eval"
|
|
3272
3311
|
});
|
|
3273
3312
|
if (!evalResult.success) {
|
|
3313
|
+
if (evalResult.validationErrors)
|
|
3314
|
+
throw new SyncValidationFailureError(evalResult.validationErrors);
|
|
3274
3315
|
throw new Error(evalResult.error || "Eval sync failed");
|
|
3275
3316
|
}
|
|
3276
3317
|
}
|
|
@@ -3343,8 +3384,11 @@ ${resources.errors.join(`
|
|
|
3343
3384
|
organizationId,
|
|
3344
3385
|
environment: "eval"
|
|
3345
3386
|
});
|
|
3346
|
-
if (!result.success)
|
|
3387
|
+
if (!result.success) {
|
|
3388
|
+
if (result.validationErrors)
|
|
3389
|
+
throw new SyncValidationFailureError(result.validationErrors);
|
|
3347
3390
|
throw new Error(result.error || "Eval sync failed");
|
|
3391
|
+
}
|
|
3348
3392
|
return result;
|
|
3349
3393
|
}
|
|
3350
3394
|
if (environment === "production") {
|
|
@@ -3353,8 +3397,11 @@ ${resources.errors.join(`
|
|
|
3353
3397
|
organizationId,
|
|
3354
3398
|
environment: "production"
|
|
3355
3399
|
});
|
|
3356
|
-
if (!result.success)
|
|
3400
|
+
if (!result.success) {
|
|
3401
|
+
if (result.validationErrors)
|
|
3402
|
+
throw new SyncValidationFailureError(result.validationErrors);
|
|
3357
3403
|
throw new Error(result.error || "Production sync failed");
|
|
3404
|
+
}
|
|
3358
3405
|
return result;
|
|
3359
3406
|
}
|
|
3360
3407
|
return performDevSync(cwd, organizationId);
|
|
@@ -3560,6 +3607,15 @@ var syncCommand = new Command5("sync").description("Sync resources to Convex and
|
|
|
3560
3607
|
}));
|
|
3561
3608
|
}
|
|
3562
3609
|
} catch (error) {
|
|
3610
|
+
if (error instanceof SyncValidationFailureError) {
|
|
3611
|
+
if (jsonMode) {
|
|
3612
|
+
console.log(JSON.stringify({ success: false, validationErrors: error.validationErrors }));
|
|
3613
|
+
} else {
|
|
3614
|
+
output.fail("Sync failed");
|
|
3615
|
+
renderValidationErrors(error.validationErrors);
|
|
3616
|
+
}
|
|
3617
|
+
process.exit(1);
|
|
3618
|
+
}
|
|
3563
3619
|
if (jsonMode) {
|
|
3564
3620
|
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
|
|
3565
3621
|
} else {
|
|
@@ -3578,7 +3634,10 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
3578
3634
|
const apiKey = getApiKey();
|
|
3579
3635
|
const nonInteractive = !isInteractive();
|
|
3580
3636
|
if (nonInteractive) {
|
|
3581
|
-
console.error("Error: struere dev
|
|
3637
|
+
console.error(chalk7.red("Error:"), "`struere dev` requires an interactive terminal (TTY) to render its watch UI.");
|
|
3638
|
+
console.error(chalk7.gray(" - For a one-shot sync, use:"), chalk7.cyan("struere sync [--env development]"));
|
|
3639
|
+
console.error(chalk7.gray(" - For continuous deployment in CI/CD, use:"), chalk7.cyan("struere deploy"));
|
|
3640
|
+
console.error(chalk7.gray(" - To run dev in the foreground, attach a TTY (avoid backgrounding with `&` or piping output)."));
|
|
3582
3641
|
process.exit(1);
|
|
3583
3642
|
}
|
|
3584
3643
|
console.log();
|
|
@@ -3690,6 +3749,9 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
3690
3749
|
console.log(chalk7.gray(" 2."), "Or run", chalk7.cyan("struere init"), "to select a different organization");
|
|
3691
3750
|
console.log();
|
|
3692
3751
|
process.exit(1);
|
|
3752
|
+
} else if (error instanceof SyncValidationFailureError) {
|
|
3753
|
+
spinner.fail("Sync failed");
|
|
3754
|
+
renderValidationErrors(error.validationErrors);
|
|
3693
3755
|
} else {
|
|
3694
3756
|
spinner.fail("Sync failed");
|
|
3695
3757
|
console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
@@ -3730,8 +3792,13 @@ var devCommand = new Command6("dev").description("Watch files and sync to develo
|
|
|
3730
3792
|
await performDevSync(cwd, project.organization.id);
|
|
3731
3793
|
syncSpinner.succeed("Synced");
|
|
3732
3794
|
} catch (error) {
|
|
3733
|
-
|
|
3734
|
-
|
|
3795
|
+
if (error instanceof SyncValidationFailureError) {
|
|
3796
|
+
syncSpinner.fail("Sync failed");
|
|
3797
|
+
renderValidationErrors(error.validationErrors);
|
|
3798
|
+
} else {
|
|
3799
|
+
syncSpinner.fail("Sync failed");
|
|
3800
|
+
console.log(chalk7.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
3801
|
+
}
|
|
3735
3802
|
}
|
|
3736
3803
|
};
|
|
3737
3804
|
const handleFileChange = (path, action) => {
|
|
@@ -3998,6 +4065,8 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
|
|
|
3998
4065
|
environment: "production"
|
|
3999
4066
|
});
|
|
4000
4067
|
if (!syncResult.success) {
|
|
4068
|
+
if (syncResult.validationErrors)
|
|
4069
|
+
throw new SyncValidationFailureError(syncResult.validationErrors);
|
|
4001
4070
|
throw new Error(syncResult.error || "Deploy failed");
|
|
4002
4071
|
}
|
|
4003
4072
|
const elapsed = Date.now() - startTime;
|
|
@@ -4107,6 +4176,14 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
|
|
|
4107
4176
|
console.log();
|
|
4108
4177
|
}
|
|
4109
4178
|
process.exit(1);
|
|
4179
|
+
} else if (error instanceof SyncValidationFailureError) {
|
|
4180
|
+
if (jsonMode) {
|
|
4181
|
+
console.log(JSON.stringify({ success: false, validationErrors: error.validationErrors }));
|
|
4182
|
+
} else {
|
|
4183
|
+
spinner.fail("Deployment failed");
|
|
4184
|
+
renderValidationErrors(error.validationErrors);
|
|
4185
|
+
}
|
|
4186
|
+
process.exit(1);
|
|
4110
4187
|
} else {
|
|
4111
4188
|
if (jsonMode) {
|
|
4112
4189
|
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }));
|
|
@@ -5776,7 +5853,8 @@ async function queryThreads(options) {
|
|
|
5776
5853
|
environment: options.environment,
|
|
5777
5854
|
...options.agentId && { agentId: options.agentId },
|
|
5778
5855
|
...options.channel && { channel: options.channel },
|
|
5779
|
-
...options.limit && { limit: options.limit }
|
|
5856
|
+
...options.limit && { limit: options.limit },
|
|
5857
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
5780
5858
|
});
|
|
5781
5859
|
if (result.error || !result.data)
|
|
5782
5860
|
return result;
|
|
@@ -5792,20 +5870,21 @@ async function queryThreads(options) {
|
|
|
5792
5870
|
}
|
|
5793
5871
|
return result;
|
|
5794
5872
|
}
|
|
5795
|
-
async function queryThreadDetail(threadId, messageLimit) {
|
|
5873
|
+
async function queryThreadDetail(threadId, messageLimit, organizationId) {
|
|
5796
5874
|
return convexQuery2("threads:getWithMessages", {
|
|
5797
5875
|
id: threadId,
|
|
5798
|
-
...messageLimit && { messageLimit }
|
|
5876
|
+
...messageLimit && { messageLimit },
|
|
5877
|
+
...organizationId && { organizationId }
|
|
5799
5878
|
});
|
|
5800
5879
|
}
|
|
5801
|
-
async function queryThreadExecutions(threadId) {
|
|
5802
|
-
return convexQuery2("executions:getByThread", { threadId });
|
|
5880
|
+
async function queryThreadExecutions(threadId, organizationId) {
|
|
5881
|
+
return convexQuery2("executions:getByThread", { threadId, ...organizationId && { organizationId } });
|
|
5803
5882
|
}
|
|
5804
|
-
async function resolveThreadId(shortId, environment) {
|
|
5883
|
+
async function resolveThreadId(shortId, environment, organizationId) {
|
|
5805
5884
|
if (shortId.includes("|") || shortId.length > 20) {
|
|
5806
5885
|
return { data: shortId };
|
|
5807
5886
|
}
|
|
5808
|
-
const result = await queryThreads({ environment: environment ?? "development", limit: 100 });
|
|
5887
|
+
const result = await queryThreads({ environment: environment ?? "development", limit: 100, organizationId });
|
|
5809
5888
|
if (result.error || !result.data)
|
|
5810
5889
|
return { error: result.error ?? "Could not fetch threads" };
|
|
5811
5890
|
const threads = result.data;
|
|
@@ -5814,7 +5893,7 @@ async function resolveThreadId(shortId, environment) {
|
|
|
5814
5893
|
for (const env of ["production", "development", "eval"]) {
|
|
5815
5894
|
if (env === (environment ?? "development"))
|
|
5816
5895
|
continue;
|
|
5817
|
-
const envResult = await queryThreads({ environment: env, limit: 100 });
|
|
5896
|
+
const envResult = await queryThreads({ environment: env, limit: 100, organizationId });
|
|
5818
5897
|
if (envResult.data) {
|
|
5819
5898
|
const envThreads = envResult.data;
|
|
5820
5899
|
const envMatch = envThreads.find((t) => t._id.endsWith(shortId));
|
|
@@ -5916,7 +5995,8 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
|
|
|
5916
5995
|
agentId,
|
|
5917
5996
|
channel: opts.channel,
|
|
5918
5997
|
phone: opts.phone,
|
|
5919
|
-
limit: parseInt(opts.limit, 10)
|
|
5998
|
+
limit: parseInt(opts.limit, 10),
|
|
5999
|
+
organizationId: orgId
|
|
5920
6000
|
});
|
|
5921
6001
|
if (error || !data) {
|
|
5922
6002
|
spinner?.fail("Failed to fetch conversations");
|
|
@@ -5953,8 +6033,9 @@ logsCommand.command("list", { isDefault: true }).description("List recent conver
|
|
|
5953
6033
|
logsCommand.command("view <thread-id>").description("View conversation messages").option("--env <environment>", "Environment hint for resolving short IDs").option("--exec", "Include execution details").option("--verbose", "Show full tool call arguments and results").option("--tail", "Show most recent messages (use with --limit)").option("--json", "Output raw JSON").option("--limit <n>", "Message limit", "100").action(async (rawThreadId, opts) => {
|
|
5954
6034
|
await ensureAuth2();
|
|
5955
6035
|
const spinner = opts.json ? null : ora12();
|
|
6036
|
+
const orgId = getOrgId2();
|
|
5956
6037
|
spinner?.start("Resolving thread");
|
|
5957
|
-
const resolved = await resolveThreadId(rawThreadId, opts.env);
|
|
6038
|
+
const resolved = await resolveThreadId(rawThreadId, opts.env, orgId);
|
|
5958
6039
|
if (resolved.error || !resolved.data) {
|
|
5959
6040
|
spinner?.fail("Thread not found");
|
|
5960
6041
|
console.log(chalk16.red("Error:"), resolved.error || `No thread matched "${rawThreadId}"`);
|
|
@@ -5964,7 +6045,7 @@ logsCommand.command("view <thread-id>").description("View conversation messages"
|
|
|
5964
6045
|
spinner?.stop();
|
|
5965
6046
|
spinner?.start("Fetching conversation");
|
|
5966
6047
|
const fetchLimit = opts.tail ? 1000 : parseInt(opts.limit, 10);
|
|
5967
|
-
const { data, error } = await queryThreadDetail(threadId, fetchLimit);
|
|
6048
|
+
const { data, error } = await queryThreadDetail(threadId, fetchLimit, orgId);
|
|
5968
6049
|
if (error || !data) {
|
|
5969
6050
|
spinner?.fail("Failed to fetch conversation");
|
|
5970
6051
|
console.log(chalk16.red("Error:"), error || "Thread not found");
|
|
@@ -5974,7 +6055,7 @@ logsCommand.command("view <thread-id>").description("View conversation messages"
|
|
|
5974
6055
|
const result = data;
|
|
5975
6056
|
let executions = [];
|
|
5976
6057
|
if (opts.exec) {
|
|
5977
|
-
const execResult = await queryThreadExecutions(threadId);
|
|
6058
|
+
const execResult = await queryThreadExecutions(threadId, orgId);
|
|
5978
6059
|
if (execResult.data) {
|
|
5979
6060
|
executions = execResult.data;
|
|
5980
6061
|
}
|
|
@@ -7593,51 +7674,54 @@ async function convexMutation5(path, args) {
|
|
|
7593
7674
|
}
|
|
7594
7675
|
return json.value;
|
|
7595
7676
|
}
|
|
7596
|
-
async function listTriggers(environment) {
|
|
7597
|
-
return convexQuery6("triggers:list", { environment });
|
|
7677
|
+
async function listTriggers(environment, organizationId) {
|
|
7678
|
+
return convexQuery6("triggers:list", { environment, ...organizationId && { organizationId } });
|
|
7598
7679
|
}
|
|
7599
|
-
async function getTrigger(slug, environment) {
|
|
7600
|
-
return convexQuery6("triggers:getBySlug", { slug, environment });
|
|
7680
|
+
async function getTrigger(slug, environment, organizationId) {
|
|
7681
|
+
return convexQuery6("triggers:getBySlug", { slug, environment, ...organizationId && { organizationId } });
|
|
7601
7682
|
}
|
|
7602
7683
|
async function listTriggerRuns(options) {
|
|
7603
7684
|
return convexQuery6("triggers:listRuns", {
|
|
7604
7685
|
environment: options.environment,
|
|
7605
7686
|
...options.status && { status: options.status },
|
|
7606
7687
|
...options.triggerSlug && { triggerSlug: options.triggerSlug },
|
|
7607
|
-
...options.limit && { limit: options.limit }
|
|
7688
|
+
...options.limit && { limit: options.limit },
|
|
7689
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
7608
7690
|
});
|
|
7609
7691
|
}
|
|
7610
|
-
async function getTriggerRunDetail(runId, environment) {
|
|
7692
|
+
async function getTriggerRunDetail(runId, environment, organizationId) {
|
|
7611
7693
|
return convexQuery6("triggers:getRunDetail", {
|
|
7612
7694
|
runId,
|
|
7613
|
-
...environment && { environment }
|
|
7695
|
+
...environment && { environment },
|
|
7696
|
+
...organizationId && { organizationId }
|
|
7614
7697
|
});
|
|
7615
7698
|
}
|
|
7616
|
-
async function getTriggerRunStats(environment) {
|
|
7617
|
-
return convexQuery6("triggers:getRunStats", { environment });
|
|
7699
|
+
async function getTriggerRunStats(environment, organizationId) {
|
|
7700
|
+
return convexQuery6("triggers:getRunStats", { environment, ...organizationId && { organizationId } });
|
|
7618
7701
|
}
|
|
7619
|
-
async function getLastRunStatuses(environment) {
|
|
7620
|
-
return convexQuery6("triggers:getLastRunStatuses", { environment });
|
|
7702
|
+
async function getLastRunStatuses(environment, organizationId) {
|
|
7703
|
+
return convexQuery6("triggers:getLastRunStatuses", { environment, ...organizationId && { organizationId } });
|
|
7621
7704
|
}
|
|
7622
|
-
async function cancelTriggerRun(runId, environment) {
|
|
7623
|
-
return convexMutation5("triggers:cancelRun", { runId, environment });
|
|
7705
|
+
async function cancelTriggerRun(runId, environment, organizationId) {
|
|
7706
|
+
return convexMutation5("triggers:cancelRun", { runId, environment, ...organizationId && { organizationId } });
|
|
7624
7707
|
}
|
|
7625
|
-
async function retryTriggerRun(runId, environment) {
|
|
7626
|
-
return convexMutation5("triggers:retryRun", { runId, environment });
|
|
7708
|
+
async function retryTriggerRun(runId, environment, organizationId) {
|
|
7709
|
+
return convexMutation5("triggers:retryRun", { runId, environment, ...organizationId && { organizationId } });
|
|
7627
7710
|
}
|
|
7628
|
-
async function toggleTrigger(slug, enabled, environment) {
|
|
7711
|
+
async function toggleTrigger(slug, enabled, environment, organizationId) {
|
|
7629
7712
|
const path = enabled ? "triggers:enable" : "triggers:disable";
|
|
7630
|
-
return convexMutation5(path, { slug, environment });
|
|
7713
|
+
return convexMutation5(path, { slug, environment, ...organizationId && { organizationId } });
|
|
7631
7714
|
}
|
|
7632
7715
|
async function listTriggerExecutions(options) {
|
|
7633
7716
|
return convexQuery6("triggers:listExecutions", {
|
|
7634
7717
|
environment: options.environment,
|
|
7635
7718
|
...options.triggerSlug && { triggerSlug: options.triggerSlug },
|
|
7636
|
-
...options.limit && { limit: options.limit }
|
|
7719
|
+
...options.limit && { limit: options.limit },
|
|
7720
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
7637
7721
|
});
|
|
7638
7722
|
}
|
|
7639
|
-
async function getTriggerExecutionDetail(eventId, environment) {
|
|
7640
|
-
return convexQuery6("triggers:getExecutionDetail", { eventId, ...environment && { environment } });
|
|
7723
|
+
async function getTriggerExecutionDetail(eventId, environment, organizationId) {
|
|
7724
|
+
return convexQuery6("triggers:getExecutionDetail", { eventId, ...environment && { environment }, ...organizationId && { organizationId } });
|
|
7641
7725
|
}
|
|
7642
7726
|
async function convexAction3(path, args) {
|
|
7643
7727
|
const token = getToken3();
|
|
@@ -7664,11 +7748,15 @@ async function convexAction3(path, args) {
|
|
|
7664
7748
|
}
|
|
7665
7749
|
return json.value;
|
|
7666
7750
|
}
|
|
7667
|
-
async function retryImmediateExecution(eventId, environment) {
|
|
7668
|
-
return convexAction3("triggers:retryImmediateExecution", { eventId, environment });
|
|
7751
|
+
async function retryImmediateExecution(eventId, environment, organizationId) {
|
|
7752
|
+
return convexAction3("triggers:retryImmediateExecution", { eventId, environment, ...organizationId && { organizationId } });
|
|
7669
7753
|
}
|
|
7670
7754
|
|
|
7671
7755
|
// src/cli/commands/triggers.ts
|
|
7756
|
+
function getOrgId3() {
|
|
7757
|
+
const project = loadProject(process.cwd());
|
|
7758
|
+
return project?.organization.id;
|
|
7759
|
+
}
|
|
7672
7760
|
async function withTriggerAuthRetry(fn) {
|
|
7673
7761
|
try {
|
|
7674
7762
|
return await fn();
|
|
@@ -7836,10 +7924,11 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
|
|
|
7836
7924
|
await ensureAuth5();
|
|
7837
7925
|
const spinner = opts.json ? null : ora14();
|
|
7838
7926
|
try {
|
|
7927
|
+
const orgId = getOrgId3();
|
|
7839
7928
|
spinner?.start("Fetching triggers");
|
|
7840
7929
|
const [triggers, statuses] = await Promise.all([
|
|
7841
|
-
listTriggers(opts.env),
|
|
7842
|
-
getLastRunStatuses(opts.env)
|
|
7930
|
+
listTriggers(opts.env, orgId),
|
|
7931
|
+
getLastRunStatuses(opts.env, orgId)
|
|
7843
7932
|
]);
|
|
7844
7933
|
let filtered = triggers;
|
|
7845
7934
|
if (opts.failed) {
|
|
@@ -7885,7 +7974,7 @@ triggersCommand.command("get <slug>").description("View trigger details").option
|
|
|
7885
7974
|
const spinner = opts.json ? null : ora14();
|
|
7886
7975
|
try {
|
|
7887
7976
|
spinner?.start("Fetching trigger");
|
|
7888
|
-
const trigger = await getTrigger(slug, opts.env);
|
|
7977
|
+
const trigger = await getTrigger(slug, opts.env, getOrgId3());
|
|
7889
7978
|
if (!trigger) {
|
|
7890
7979
|
spinner?.fail("Trigger not found");
|
|
7891
7980
|
if (opts.json) {
|
|
@@ -7968,7 +8057,8 @@ triggersCommand.command("runs [slug]").description("List trigger runs").option("
|
|
|
7968
8057
|
environment: opts.env,
|
|
7969
8058
|
status: opts.status,
|
|
7970
8059
|
triggerSlug: slug,
|
|
7971
|
-
limit: parseInt(opts.limit, 10)
|
|
8060
|
+
limit: parseInt(opts.limit, 10),
|
|
8061
|
+
organizationId: getOrgId3()
|
|
7972
8062
|
});
|
|
7973
8063
|
spinner?.succeed(`Found ${runs.length} runs`);
|
|
7974
8064
|
if (opts.json) {
|
|
@@ -8010,7 +8100,7 @@ triggersCommand.command("run <run-id>").description("View trigger run details").
|
|
|
8010
8100
|
const spinner = opts.json ? null : ora14();
|
|
8011
8101
|
try {
|
|
8012
8102
|
spinner?.start("Fetching run");
|
|
8013
|
-
const run = await getTriggerRunDetail(runId, opts.env);
|
|
8103
|
+
const run = await getTriggerRunDetail(runId, opts.env, getOrgId3());
|
|
8014
8104
|
if (!run) {
|
|
8015
8105
|
spinner?.fail("Run not found");
|
|
8016
8106
|
if (opts.json) {
|
|
@@ -8054,8 +8144,9 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
|
|
|
8054
8144
|
await ensureAuth5();
|
|
8055
8145
|
const spinner = opts.json ? null : ora14();
|
|
8056
8146
|
try {
|
|
8147
|
+
const orgId = getOrgId3();
|
|
8057
8148
|
spinner?.start("Fetching statistics");
|
|
8058
|
-
const stats = await getTriggerRunStats(opts.env);
|
|
8149
|
+
const stats = await getTriggerRunStats(opts.env, orgId);
|
|
8059
8150
|
spinner?.succeed("Run statistics");
|
|
8060
8151
|
if (opts.json) {
|
|
8061
8152
|
console.log(JSON.stringify(stats, null, 2));
|
|
@@ -8080,7 +8171,7 @@ triggersCommand.command("stats").description("Show trigger run statistics").opti
|
|
|
8080
8171
|
console.log(` ${chalk20.gray("\u2500".repeat(18))}`);
|
|
8081
8172
|
console.log(` ${chalk20.bold("Total".padEnd(12))} ${chalk20.bold(String(total).padStart(6))}`);
|
|
8082
8173
|
console.log();
|
|
8083
|
-
const executions = await listTriggerExecutions({ environment: opts.env, limit: 50 });
|
|
8174
|
+
const executions = await listTriggerExecutions({ environment: opts.env, limit: 50, organizationId: orgId });
|
|
8084
8175
|
const recentFailures = executions.filter((e) => e.eventType === "trigger.failed");
|
|
8085
8176
|
const recentSuccesses = executions.length - recentFailures.length;
|
|
8086
8177
|
console.log(chalk20.bold(` Recent Executions`));
|
|
@@ -8112,7 +8203,8 @@ triggersCommand.command("logs [slug]").description("View trigger execution histo
|
|
|
8112
8203
|
const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
|
|
8113
8204
|
environment: opts.env,
|
|
8114
8205
|
triggerSlug: slug,
|
|
8115
|
-
limit: parseInt(opts.limit, 10)
|
|
8206
|
+
limit: parseInt(opts.limit, 10),
|
|
8207
|
+
organizationId: getOrgId3()
|
|
8116
8208
|
}));
|
|
8117
8209
|
spinner?.succeed(`Found ${executions.length} executions`);
|
|
8118
8210
|
if (opts.json) {
|
|
@@ -8172,6 +8264,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
|
|
|
8172
8264
|
await ensureAuth5();
|
|
8173
8265
|
const spinner = opts.json ? null : ora14();
|
|
8174
8266
|
try {
|
|
8267
|
+
const orgId = getOrgId3();
|
|
8175
8268
|
const nth = parseInt(opts.nth, 10);
|
|
8176
8269
|
if (isNaN(nth) || nth < 1) {
|
|
8177
8270
|
console.error(chalk20.red("Error:"), "--nth must be a positive integer (e.g., --nth 1 for most recent)");
|
|
@@ -8184,7 +8277,8 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
|
|
|
8184
8277
|
const executions = await withTriggerAuthRetry(() => listTriggerExecutions({
|
|
8185
8278
|
environment: opts.env,
|
|
8186
8279
|
triggerSlug: identifier,
|
|
8187
|
-
limit: nth
|
|
8280
|
+
limit: nth,
|
|
8281
|
+
organizationId: orgId
|
|
8188
8282
|
}));
|
|
8189
8283
|
if (!executions.length) {
|
|
8190
8284
|
spinner?.fail(`No executions found for trigger "${identifier}" in ${opts.env}`);
|
|
@@ -8202,7 +8296,7 @@ triggersCommand.command("log <identifier>").description("View detailed trigger e
|
|
|
8202
8296
|
spinner?.succeed(`Found execution for "${identifier}"`);
|
|
8203
8297
|
}
|
|
8204
8298
|
spinner?.start("Fetching execution detail");
|
|
8205
|
-
const event = await withTriggerAuthRetry(() => getTriggerExecutionDetail(eventId, opts.env));
|
|
8299
|
+
const event = await withTriggerAuthRetry(() => getTriggerExecutionDetail(eventId, opts.env, orgId));
|
|
8206
8300
|
if (!event) {
|
|
8207
8301
|
spinner?.fail("Execution not found");
|
|
8208
8302
|
if (opts.json) {
|
|
@@ -8264,7 +8358,7 @@ triggersCommand.command("retry <run-id>").description("Retry a failed or dead ru
|
|
|
8264
8358
|
}
|
|
8265
8359
|
try {
|
|
8266
8360
|
spinner?.start("Retrying run...");
|
|
8267
|
-
await retryTriggerRun(runId, environment);
|
|
8361
|
+
await retryTriggerRun(runId, environment, getOrgId3());
|
|
8268
8362
|
spinner?.succeed(chalk20.green(`Run ${runId} queued for retry`));
|
|
8269
8363
|
if (opts.json) {
|
|
8270
8364
|
console.log(JSON.stringify({ success: true, runId }));
|
|
@@ -8295,7 +8389,7 @@ triggersCommand.command("cancel <run-id>").description("Cancel a pending run").o
|
|
|
8295
8389
|
}
|
|
8296
8390
|
try {
|
|
8297
8391
|
spinner?.start("Cancelling run...");
|
|
8298
|
-
await cancelTriggerRun(runId, environment);
|
|
8392
|
+
await cancelTriggerRun(runId, environment, getOrgId3());
|
|
8299
8393
|
spinner?.succeed(chalk20.green(`Run ${runId} cancelled`));
|
|
8300
8394
|
if (opts.json) {
|
|
8301
8395
|
console.log(JSON.stringify({ success: true, runId }));
|
|
@@ -8326,7 +8420,7 @@ triggersCommand.command("enable <slug>").description("Enable a trigger").option(
|
|
|
8326
8420
|
}
|
|
8327
8421
|
try {
|
|
8328
8422
|
spinner?.start("Enabling trigger...");
|
|
8329
|
-
await toggleTrigger(slug, true, environment);
|
|
8423
|
+
await toggleTrigger(slug, true, environment, getOrgId3());
|
|
8330
8424
|
spinner?.succeed(chalk20.green(`Trigger ${slug} enabled`));
|
|
8331
8425
|
if (opts.json) {
|
|
8332
8426
|
console.log(JSON.stringify({ success: true, slug }));
|
|
@@ -8357,7 +8451,7 @@ triggersCommand.command("disable <slug>").description("Disable a trigger").optio
|
|
|
8357
8451
|
}
|
|
8358
8452
|
try {
|
|
8359
8453
|
spinner?.start("Disabling trigger...");
|
|
8360
|
-
await toggleTrigger(slug, false, environment);
|
|
8454
|
+
await toggleTrigger(slug, false, environment, getOrgId3());
|
|
8361
8455
|
spinner?.succeed(chalk20.green(`Trigger ${slug} disabled`));
|
|
8362
8456
|
if (opts.json) {
|
|
8363
8457
|
console.log(JSON.stringify({ success: true, slug }));
|
|
@@ -8388,7 +8482,7 @@ triggersCommand.command("retry-event <event-id>").description("Retry a failed im
|
|
|
8388
8482
|
}
|
|
8389
8483
|
try {
|
|
8390
8484
|
spinner?.start("Retrying failed execution...");
|
|
8391
|
-
const result = await retryImmediateExecution(eventId, environment);
|
|
8485
|
+
const result = await retryImmediateExecution(eventId, environment, getOrgId3());
|
|
8392
8486
|
if (result.success) {
|
|
8393
8487
|
spinner?.succeed(chalk20.green(`Execution retried successfully`));
|
|
8394
8488
|
} else {
|
|
@@ -8436,7 +8530,8 @@ triggersCommand.command("fire <slug>").description("Manually fire a trigger").op
|
|
|
8436
8530
|
slug,
|
|
8437
8531
|
environment,
|
|
8438
8532
|
entityId: opts.entity,
|
|
8439
|
-
data
|
|
8533
|
+
data,
|
|
8534
|
+
organizationId: getOrgId3()
|
|
8440
8535
|
});
|
|
8441
8536
|
if (error) {
|
|
8442
8537
|
spinner?.fail("Trigger execution failed");
|
|
@@ -8594,12 +8689,14 @@ async function listThreads(options) {
|
|
|
8594
8689
|
return convexQuery7("threads:listWithPreviews", {
|
|
8595
8690
|
environment: options.environment,
|
|
8596
8691
|
channel: options.channel,
|
|
8597
|
-
limit: options.limit
|
|
8692
|
+
limit: options.limit,
|
|
8693
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
8598
8694
|
});
|
|
8599
8695
|
}
|
|
8600
8696
|
async function getThreadWithMessages(options) {
|
|
8601
8697
|
return convexQuery7("threads:getWithMessages", {
|
|
8602
|
-
id: options.threadId
|
|
8698
|
+
id: options.threadId,
|
|
8699
|
+
...options.organizationId && { organizationId: options.organizationId }
|
|
8603
8700
|
});
|
|
8604
8701
|
}
|
|
8605
8702
|
async function archiveThread(options) {
|
|
@@ -10377,8 +10474,8 @@ function checkModelConfig(resources) {
|
|
|
10377
10474
|
hint: "Check agent model config - temperature should be 0-2"
|
|
10378
10475
|
};
|
|
10379
10476
|
}
|
|
10380
|
-
async function checkStuckThreads(environment) {
|
|
10381
|
-
const threads = await withAuthRetry2(() => listThreads({ environment, limit: 100 }));
|
|
10477
|
+
async function checkStuckThreads(environment, organizationId) {
|
|
10478
|
+
const threads = await withAuthRetry2(() => listThreads({ environment, limit: 100, organizationId }));
|
|
10382
10479
|
const issues = [];
|
|
10383
10480
|
const now = Date.now();
|
|
10384
10481
|
const fiveMinutes = 5 * 60 * 1000;
|
|
@@ -10407,8 +10504,8 @@ async function checkStuckThreads(environment) {
|
|
|
10407
10504
|
hint: "Run `struere threads archive <id>` to archive stuck threads"
|
|
10408
10505
|
};
|
|
10409
10506
|
}
|
|
10410
|
-
async function checkTriggerHealth(environment) {
|
|
10411
|
-
const executions = await withAuthRetry2(() => listTriggerExecutions({ environment, limit: 100 }));
|
|
10507
|
+
async function checkTriggerHealth(environment, organizationId) {
|
|
10508
|
+
const executions = await withAuthRetry2(() => listTriggerExecutions({ environment, limit: 100, organizationId }));
|
|
10412
10509
|
if (executions.length === 0) {
|
|
10413
10510
|
return {
|
|
10414
10511
|
id: "trigger-health",
|
|
@@ -10455,8 +10552,8 @@ async function checkTriggerHealth(environment) {
|
|
|
10455
10552
|
hint: "Run `struere triggers logs <slug>` to investigate failures"
|
|
10456
10553
|
};
|
|
10457
10554
|
}
|
|
10458
|
-
async function checkSyncDrift(resources, environment) {
|
|
10459
|
-
const { state, error } = await withAuthRetry2(() => getSyncState(
|
|
10555
|
+
async function checkSyncDrift(resources, environment, organizationId) {
|
|
10556
|
+
const { state, error } = await withAuthRetry2(() => getSyncState(organizationId, environment));
|
|
10460
10557
|
if (error || !state) {
|
|
10461
10558
|
return {
|
|
10462
10559
|
id: "sync-drift",
|
|
@@ -10565,6 +10662,8 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
10565
10662
|
const spinner = opts.json ? null : ora20();
|
|
10566
10663
|
const cwd = process.cwd();
|
|
10567
10664
|
const environment = opts.env;
|
|
10665
|
+
const project = loadProject(cwd);
|
|
10666
|
+
const organizationId = project?.organization.id;
|
|
10568
10667
|
try {
|
|
10569
10668
|
spinner?.start("Loading resources...");
|
|
10570
10669
|
const resources = await loadAllResources(cwd);
|
|
@@ -10575,9 +10674,9 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
10575
10674
|
runCheck(() => checkTemplateApprovals(resources)),
|
|
10576
10675
|
runCheck(() => checkEntityTypeReferences(resources)),
|
|
10577
10676
|
runCheck(() => checkModelConfig(resources)),
|
|
10578
|
-
runCheck(() => checkStuckThreads(environment)),
|
|
10579
|
-
runCheck(() => checkTriggerHealth(environment)),
|
|
10580
|
-
runCheck(() => checkSyncDrift(resources, environment))
|
|
10677
|
+
runCheck(() => checkStuckThreads(environment, organizationId)),
|
|
10678
|
+
runCheck(() => checkTriggerHealth(environment, organizationId)),
|
|
10679
|
+
runCheck(() => checkSyncDrift(resources, environment, organizationId))
|
|
10581
10680
|
]);
|
|
10582
10681
|
spinner?.stop();
|
|
10583
10682
|
const checks = results.map((r) => r.status === "fulfilled" ? r.value : { id: "unknown", label: "Unknown", status: "warn", message: `Check failed: ${r.reason}` });
|
|
@@ -10631,10 +10730,332 @@ var doctorCommand = new Command25("doctor").description("Run diagnostic checks o
|
|
|
10631
10730
|
process.exit(1);
|
|
10632
10731
|
}
|
|
10633
10732
|
});
|
|
10733
|
+
|
|
10734
|
+
// src/cli/commands/keys.ts
|
|
10735
|
+
init_credentials();
|
|
10736
|
+
import { Command as Command26 } from "commander";
|
|
10737
|
+
import chalk29 from "chalk";
|
|
10738
|
+
import ora21 from "ora";
|
|
10739
|
+
import { input as input3, confirm as confirm8 } from "@inquirer/prompts";
|
|
10740
|
+
init_convex();
|
|
10741
|
+
|
|
10742
|
+
// src/cli/utils/apiKeys.ts
|
|
10743
|
+
init_credentials();
|
|
10744
|
+
init_config();
|
|
10745
|
+
function getToken5() {
|
|
10746
|
+
const credentials = loadCredentials();
|
|
10747
|
+
const apiKey = getApiKey();
|
|
10748
|
+
const token = apiKey || credentials?.token;
|
|
10749
|
+
if (!token)
|
|
10750
|
+
throw new Error("Not authenticated");
|
|
10751
|
+
return token;
|
|
10752
|
+
}
|
|
10753
|
+
async function convexQuery8(path, args) {
|
|
10754
|
+
const token = getToken5();
|
|
10755
|
+
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
10756
|
+
method: "POST",
|
|
10757
|
+
headers: {
|
|
10758
|
+
"Content-Type": "application/json",
|
|
10759
|
+
Authorization: `Bearer ${token}`
|
|
10760
|
+
},
|
|
10761
|
+
body: JSON.stringify({ path, args })
|
|
10762
|
+
});
|
|
10763
|
+
const text = await response.text();
|
|
10764
|
+
let json;
|
|
10765
|
+
try {
|
|
10766
|
+
json = JSON.parse(text);
|
|
10767
|
+
} catch {
|
|
10768
|
+
throw new Error(text || `HTTP ${response.status}`);
|
|
10769
|
+
}
|
|
10770
|
+
if (!response.ok) {
|
|
10771
|
+
throw new Error(json.errorData?.message || json.errorMessage || text);
|
|
10772
|
+
}
|
|
10773
|
+
if (json.status === "error") {
|
|
10774
|
+
throw new Error(json.errorMessage || "Unknown error from Convex");
|
|
10775
|
+
}
|
|
10776
|
+
return json.value;
|
|
10777
|
+
}
|
|
10778
|
+
async function convexMutation6(path, args) {
|
|
10779
|
+
const token = getToken5();
|
|
10780
|
+
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
10781
|
+
method: "POST",
|
|
10782
|
+
headers: {
|
|
10783
|
+
"Content-Type": "application/json",
|
|
10784
|
+
Authorization: `Bearer ${token}`
|
|
10785
|
+
},
|
|
10786
|
+
body: JSON.stringify({ path, args })
|
|
10787
|
+
});
|
|
10788
|
+
const text = await response.text();
|
|
10789
|
+
let json;
|
|
10790
|
+
try {
|
|
10791
|
+
json = JSON.parse(text);
|
|
10792
|
+
} catch {
|
|
10793
|
+
throw new Error(text || `HTTP ${response.status}`);
|
|
10794
|
+
}
|
|
10795
|
+
if (!response.ok) {
|
|
10796
|
+
throw new Error(json.errorData?.message || json.errorMessage || text);
|
|
10797
|
+
}
|
|
10798
|
+
if (json.status === "error") {
|
|
10799
|
+
throw new Error(json.errorMessage || "Unknown error from Convex");
|
|
10800
|
+
}
|
|
10801
|
+
return json.value;
|
|
10802
|
+
}
|
|
10803
|
+
async function listApiKeys() {
|
|
10804
|
+
return convexQuery8("apiKeys:list", {});
|
|
10805
|
+
}
|
|
10806
|
+
async function createApiKey(args) {
|
|
10807
|
+
return convexMutation6("apiKeys:create", {
|
|
10808
|
+
name: args.name,
|
|
10809
|
+
environment: args.environment,
|
|
10810
|
+
permissions: args.permissions ?? ["*"]
|
|
10811
|
+
});
|
|
10812
|
+
}
|
|
10813
|
+
async function removeApiKey(id) {
|
|
10814
|
+
return convexMutation6("apiKeys:remove", { id });
|
|
10815
|
+
}
|
|
10816
|
+
|
|
10817
|
+
// src/cli/commands/keys.ts
|
|
10818
|
+
async function withKeyAuthRetry(fn) {
|
|
10819
|
+
try {
|
|
10820
|
+
return await fn();
|
|
10821
|
+
} catch (err) {
|
|
10822
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10823
|
+
if (msg.includes("Unauthenticated") || msg.includes("OIDC") || msg.includes("token") || msg.includes("expired")) {
|
|
10824
|
+
const refreshed = await refreshToken();
|
|
10825
|
+
if (!refreshed)
|
|
10826
|
+
throw err;
|
|
10827
|
+
return fn();
|
|
10828
|
+
}
|
|
10829
|
+
throw err;
|
|
10830
|
+
}
|
|
10831
|
+
}
|
|
10832
|
+
async function ensureAuth9() {
|
|
10833
|
+
const cwd = process.cwd();
|
|
10834
|
+
const nonInteractive = !isInteractive();
|
|
10835
|
+
if (!hasProject(cwd)) {
|
|
10836
|
+
if (nonInteractive) {
|
|
10837
|
+
console.error(chalk29.red("No struere.json found. Run struere init first."));
|
|
10838
|
+
process.exit(1);
|
|
10839
|
+
}
|
|
10840
|
+
console.log(chalk29.yellow("No struere.json found - initializing project..."));
|
|
10841
|
+
console.log();
|
|
10842
|
+
const success = await runInit(cwd);
|
|
10843
|
+
if (!success) {
|
|
10844
|
+
process.exit(1);
|
|
10845
|
+
}
|
|
10846
|
+
console.log();
|
|
10847
|
+
}
|
|
10848
|
+
let credentials = loadCredentials();
|
|
10849
|
+
const apiKey = getApiKey();
|
|
10850
|
+
if (!credentials && !apiKey) {
|
|
10851
|
+
if (nonInteractive) {
|
|
10852
|
+
console.error(chalk29.red("Not authenticated. Set STRUERE_API_KEY or run struere login."));
|
|
10853
|
+
process.exit(1);
|
|
10854
|
+
}
|
|
10855
|
+
console.log(chalk29.yellow("Not logged in - authenticating..."));
|
|
10856
|
+
console.log();
|
|
10857
|
+
credentials = await performLogin();
|
|
10858
|
+
if (!credentials) {
|
|
10859
|
+
console.log(chalk29.red("Authentication failed"));
|
|
10860
|
+
process.exit(1);
|
|
10861
|
+
}
|
|
10862
|
+
console.log();
|
|
10863
|
+
}
|
|
10864
|
+
return true;
|
|
10865
|
+
}
|
|
10866
|
+
function relativeTime4(ts) {
|
|
10867
|
+
const diff = Date.now() - ts;
|
|
10868
|
+
const seconds = Math.floor(diff / 1000);
|
|
10869
|
+
if (seconds < 60)
|
|
10870
|
+
return `${seconds}s ago`;
|
|
10871
|
+
const minutes = Math.floor(seconds / 60);
|
|
10872
|
+
if (minutes < 60)
|
|
10873
|
+
return `${minutes}m ago`;
|
|
10874
|
+
const hours = Math.floor(minutes / 60);
|
|
10875
|
+
if (hours < 24)
|
|
10876
|
+
return `${hours}h ago`;
|
|
10877
|
+
const days = Math.floor(hours / 24);
|
|
10878
|
+
return `${days}d ago`;
|
|
10879
|
+
}
|
|
10880
|
+
function maskedPrefix(prefix) {
|
|
10881
|
+
return `${prefix}****`;
|
|
10882
|
+
}
|
|
10883
|
+
function isValidEnvironment(value) {
|
|
10884
|
+
return value === "development" || value === "production" || value === "eval";
|
|
10885
|
+
}
|
|
10886
|
+
var keysCommand = new Command26("keys").description("Manage API keys");
|
|
10887
|
+
keysCommand.command("list", { isDefault: true }).description("List API keys").option("--env <environment>", "Filter by environment (development|production|eval)").option("--json", "Output raw JSON").action(async (opts) => {
|
|
10888
|
+
await ensureAuth9();
|
|
10889
|
+
const spinner = opts.json ? null : ora21();
|
|
10890
|
+
try {
|
|
10891
|
+
spinner?.start("Fetching API keys");
|
|
10892
|
+
let keys = await withKeyAuthRetry(() => listApiKeys());
|
|
10893
|
+
if (opts.env) {
|
|
10894
|
+
if (!isValidEnvironment(opts.env)) {
|
|
10895
|
+
spinner?.fail(`Invalid environment: ${opts.env}`);
|
|
10896
|
+
process.exit(1);
|
|
10897
|
+
}
|
|
10898
|
+
keys = keys.filter((k) => k.environment === opts.env);
|
|
10899
|
+
}
|
|
10900
|
+
spinner?.succeed(`Found ${keys.length} API key${keys.length === 1 ? "" : "s"}${opts.env ? ` (${opts.env})` : ""}`);
|
|
10901
|
+
if (opts.json) {
|
|
10902
|
+
console.log(JSON.stringify(keys, null, 2));
|
|
10903
|
+
return;
|
|
10904
|
+
}
|
|
10905
|
+
console.log();
|
|
10906
|
+
renderTable([
|
|
10907
|
+
{ key: "id", label: "ID", width: 14 },
|
|
10908
|
+
{ key: "name", label: "Name", width: 24 },
|
|
10909
|
+
{ key: "prefix", label: "Prefix", width: 22 },
|
|
10910
|
+
{ key: "environment", label: "Env", width: 12 },
|
|
10911
|
+
{ key: "created", label: "Created", width: 12 },
|
|
10912
|
+
{ key: "lastUsed", label: "Last Used", width: 12 }
|
|
10913
|
+
], keys.map((k) => ({
|
|
10914
|
+
id: k.id.slice(-12),
|
|
10915
|
+
name: k.name,
|
|
10916
|
+
prefix: maskedPrefix(k.keyPrefix),
|
|
10917
|
+
environment: k.environment,
|
|
10918
|
+
created: relativeTime4(k.createdAt),
|
|
10919
|
+
lastUsed: k.lastUsedAt ? relativeTime4(k.lastUsedAt) : chalk29.gray("-")
|
|
10920
|
+
})));
|
|
10921
|
+
console.log();
|
|
10922
|
+
} catch (err) {
|
|
10923
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10924
|
+
spinner?.fail("Failed to fetch API keys");
|
|
10925
|
+
if (opts.json) {
|
|
10926
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
10927
|
+
} else {
|
|
10928
|
+
console.log(chalk29.red("Error:"), message);
|
|
10929
|
+
}
|
|
10930
|
+
process.exit(1);
|
|
10931
|
+
}
|
|
10932
|
+
});
|
|
10933
|
+
keysCommand.command("create").description("Create a new API key").option("--name <name>", "Key name").option("--env <environment>", "Environment (development|production|eval)", "development").option("--json", "Output raw JSON").option("--confirm", "Skip production confirmation").action(async (opts) => {
|
|
10934
|
+
await ensureAuth9();
|
|
10935
|
+
if (!isValidEnvironment(opts.env)) {
|
|
10936
|
+
const message = `Invalid environment: ${opts.env}`;
|
|
10937
|
+
if (opts.json) {
|
|
10938
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
10939
|
+
} else {
|
|
10940
|
+
console.log(chalk29.red("Error:"), message);
|
|
10941
|
+
}
|
|
10942
|
+
process.exit(1);
|
|
10943
|
+
}
|
|
10944
|
+
const environment = opts.env;
|
|
10945
|
+
let name = opts.name;
|
|
10946
|
+
if (!name || !name.trim()) {
|
|
10947
|
+
if (!isInteractive()) {
|
|
10948
|
+
const message = "--name is required in non-interactive mode";
|
|
10949
|
+
if (opts.json) {
|
|
10950
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
10951
|
+
} else {
|
|
10952
|
+
console.log(chalk29.red("Error:"), message);
|
|
10953
|
+
}
|
|
10954
|
+
process.exit(1);
|
|
10955
|
+
}
|
|
10956
|
+
name = await input3({
|
|
10957
|
+
message: "API key name:",
|
|
10958
|
+
validate: (v) => v.trim().length > 0 || "Name is required"
|
|
10959
|
+
});
|
|
10960
|
+
}
|
|
10961
|
+
name = name.trim();
|
|
10962
|
+
if (environment === "production" && !opts.confirm && !opts.json && isInteractive()) {
|
|
10963
|
+
const ok = await confirm8({
|
|
10964
|
+
message: chalk29.yellow("Create API key in PRODUCTION environment?"),
|
|
10965
|
+
default: false
|
|
10966
|
+
});
|
|
10967
|
+
if (!ok) {
|
|
10968
|
+
console.log(chalk29.gray("Cancelled"));
|
|
10969
|
+
process.exit(0);
|
|
10970
|
+
}
|
|
10971
|
+
}
|
|
10972
|
+
const spinner = opts.json ? null : ora21();
|
|
10973
|
+
try {
|
|
10974
|
+
spinner?.start("Creating API key");
|
|
10975
|
+
const result = await withKeyAuthRetry(() => createApiKey({ name, environment }));
|
|
10976
|
+
spinner?.succeed(chalk29.green(`Created API key ${chalk29.cyan(result.name)}`));
|
|
10977
|
+
if (opts.json) {
|
|
10978
|
+
console.log(JSON.stringify({
|
|
10979
|
+
id: result.id,
|
|
10980
|
+
name: result.name,
|
|
10981
|
+
prefix: result.keyPrefix,
|
|
10982
|
+
key: result.key,
|
|
10983
|
+
environment,
|
|
10984
|
+
createdAt: result.createdAt
|
|
10985
|
+
}, null, 2));
|
|
10986
|
+
return;
|
|
10987
|
+
}
|
|
10988
|
+
console.log();
|
|
10989
|
+
console.log(chalk29.bold(" " + chalk29.yellow("Save this key now \u2014 it will not be shown again.")));
|
|
10990
|
+
console.log();
|
|
10991
|
+
console.log(` ${chalk29.gray("Key:")} ${chalk29.cyan(result.key)}`);
|
|
10992
|
+
console.log(` ${chalk29.gray("Name:")} ${result.name}`);
|
|
10993
|
+
console.log(` ${chalk29.gray("Prefix:")} ${result.keyPrefix}`);
|
|
10994
|
+
console.log(` ${chalk29.gray("Environment:")} ${environment}`);
|
|
10995
|
+
console.log(` ${chalk29.gray("ID:")} ${result.id}`);
|
|
10996
|
+
console.log();
|
|
10997
|
+
} catch (err) {
|
|
10998
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10999
|
+
spinner?.fail("Failed to create API key");
|
|
11000
|
+
if (opts.json) {
|
|
11001
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
11002
|
+
} else {
|
|
11003
|
+
console.log(chalk29.red("Error:"), message);
|
|
11004
|
+
}
|
|
11005
|
+
process.exit(1);
|
|
11006
|
+
}
|
|
11007
|
+
});
|
|
11008
|
+
keysCommand.command("revoke <id-or-prefix>").description("Revoke an API key (delete)").option("--confirm", "Skip confirmation prompt").option("--json", "Output raw JSON").action(async (idOrPrefix, opts) => {
|
|
11009
|
+
await ensureAuth9();
|
|
11010
|
+
const spinner = opts.json ? null : ora21();
|
|
11011
|
+
try {
|
|
11012
|
+
spinner?.start("Looking up API key");
|
|
11013
|
+
const keys = await withKeyAuthRetry(() => listApiKeys());
|
|
11014
|
+
const target = keys.find((k) => k.id === idOrPrefix) ?? keys.find((k) => k.id.endsWith(idOrPrefix)) ?? keys.find((k) => k.keyPrefix === idOrPrefix) ?? keys.find((k) => k.keyPrefix.startsWith(idOrPrefix));
|
|
11015
|
+
if (!target) {
|
|
11016
|
+
spinner?.fail(`API key not found: ${idOrPrefix}`);
|
|
11017
|
+
if (opts.json) {
|
|
11018
|
+
console.log(JSON.stringify({ success: false, error: "API key not found" }));
|
|
11019
|
+
}
|
|
11020
|
+
process.exit(1);
|
|
11021
|
+
}
|
|
11022
|
+
spinner?.succeed(`Found ${chalk29.cyan(target.name)} (${maskedPrefix(target.keyPrefix)}, ${target.environment})`);
|
|
11023
|
+
if (!opts.confirm && !opts.json) {
|
|
11024
|
+
if (!isInteractive()) {
|
|
11025
|
+
console.log(chalk29.red("Error:"), "Use --confirm to revoke in non-interactive mode");
|
|
11026
|
+
process.exit(1);
|
|
11027
|
+
}
|
|
11028
|
+
const ok = await confirm8({
|
|
11029
|
+
message: `Revoke this key? This cannot be undone.`,
|
|
11030
|
+
default: false
|
|
11031
|
+
});
|
|
11032
|
+
if (!ok) {
|
|
11033
|
+
console.log(chalk29.gray("Cancelled"));
|
|
11034
|
+
process.exit(0);
|
|
11035
|
+
}
|
|
11036
|
+
}
|
|
11037
|
+
const revokeSpinner = opts.json ? null : ora21();
|
|
11038
|
+
revokeSpinner?.start("Revoking API key");
|
|
11039
|
+
await withKeyAuthRetry(() => removeApiKey(target.id));
|
|
11040
|
+
revokeSpinner?.succeed(chalk29.green(`Revoked ${chalk29.cyan(target.name)}`));
|
|
11041
|
+
if (opts.json) {
|
|
11042
|
+
console.log(JSON.stringify({ success: true, id: target.id, name: target.name }));
|
|
11043
|
+
}
|
|
11044
|
+
} catch (err) {
|
|
11045
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11046
|
+
spinner?.fail("Failed to revoke API key");
|
|
11047
|
+
if (opts.json) {
|
|
11048
|
+
console.log(JSON.stringify({ success: false, error: message }));
|
|
11049
|
+
} else {
|
|
11050
|
+
console.log(chalk29.red("Error:"), message);
|
|
11051
|
+
}
|
|
11052
|
+
process.exit(1);
|
|
11053
|
+
}
|
|
11054
|
+
});
|
|
10634
11055
|
// package.json
|
|
10635
11056
|
var package_default = {
|
|
10636
11057
|
name: "struere",
|
|
10637
|
-
version: "0.14.
|
|
11058
|
+
version: "0.14.3",
|
|
10638
11059
|
description: "Build, test, and deploy AI agents",
|
|
10639
11060
|
keywords: [
|
|
10640
11061
|
"ai",
|
|
@@ -10669,13 +11090,17 @@ var package_default = {
|
|
|
10669
11090
|
".": {
|
|
10670
11091
|
import: "./dist/index.js",
|
|
10671
11092
|
types: "./dist/index.d.ts"
|
|
11093
|
+
},
|
|
11094
|
+
"./client": {
|
|
11095
|
+
import: "./dist/client/index.js",
|
|
11096
|
+
types: "./dist/client/index.d.ts"
|
|
10672
11097
|
}
|
|
10673
11098
|
},
|
|
10674
11099
|
files: [
|
|
10675
11100
|
"dist"
|
|
10676
11101
|
],
|
|
10677
11102
|
scripts: {
|
|
10678
|
-
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
11103
|
+
build: "bun build ./src/cli/index.ts --outdir ./dist/cli --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && bun build ./src/index.ts --outdir ./dist --target node && bun build ./src/client/index.ts --outdir ./dist/client --target browser && bun build ./src/bin/struere.ts --outdir ./dist/bin --target bun --external commander --external chalk --external ora --external chokidar --external yaml --external @inquirer/prompts && tsc --emitDeclarationOnly && chmod +x ./dist/bin/struere.js",
|
|
10679
11104
|
dev: "tsc --watch",
|
|
10680
11105
|
test: "bun test",
|
|
10681
11106
|
prepublishOnly: "bun run build"
|
|
@@ -10759,4 +11184,5 @@ program.addCommand(chatCommand);
|
|
|
10759
11184
|
program.addCommand(whatsappCommand);
|
|
10760
11185
|
program.addCommand(diffCommand);
|
|
10761
11186
|
program.addCommand(doctorCommand);
|
|
11187
|
+
program.addCommand(keysCommand);
|
|
10762
11188
|
program.parse();
|