thinkwork-cli 0.12.0 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -1744,13 +1744,66 @@ function finalizeAws(profile, mode) {
|
|
|
1744
1744
|
` Override per-command with --profile <other>, or unset with \`rm ~/.thinkwork/config.json\`.`
|
|
1745
1745
|
)
|
|
1746
1746
|
);
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
);
|
|
1751
|
-
|
|
1752
|
-
|
|
1747
|
+
return identity;
|
|
1748
|
+
}
|
|
1749
|
+
async function offerApiLoginChain(opts) {
|
|
1750
|
+
const stages = listDeployedStages(opts.region);
|
|
1751
|
+
const candidates = stages.filter(
|
|
1752
|
+
(stage) => loadStageSession(stage) === null
|
|
1753
1753
|
);
|
|
1754
|
+
if (candidates.length === 0 || !isInteractive()) {
|
|
1755
|
+
console.log("");
|
|
1756
|
+
console.log(
|
|
1757
|
+
` ${chalk7.bold("Next:")} run ${chalk7.cyan("thinkwork login --stage <stage>")} if you also need`
|
|
1758
|
+
);
|
|
1759
|
+
console.log(
|
|
1760
|
+
` an API session (required for ${chalk7.cyan("eval")}, ${chalk7.cyan("agent")}, ${chalk7.cyan("thread")}, etc.).`
|
|
1761
|
+
);
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
console.log("");
|
|
1765
|
+
let chosen;
|
|
1766
|
+
if (candidates.length === 1) {
|
|
1767
|
+
const stage = candidates[0];
|
|
1768
|
+
const answer = await select2({
|
|
1769
|
+
message: `Also sign in to the API for stage "${stage}"? (required for eval / agent / thread)`,
|
|
1770
|
+
choices: [
|
|
1771
|
+
{ name: `Yes \u2014 sign in to ${stage}`, value: stage },
|
|
1772
|
+
{ name: "No \u2014 skip", value: null }
|
|
1773
|
+
]
|
|
1774
|
+
});
|
|
1775
|
+
chosen = answer ?? null;
|
|
1776
|
+
} else {
|
|
1777
|
+
const skip = "__skip__";
|
|
1778
|
+
const answer = await select2({
|
|
1779
|
+
message: "Also sign in to an API stage now?",
|
|
1780
|
+
choices: [
|
|
1781
|
+
...candidates.map((stage) => ({
|
|
1782
|
+
name: `Yes \u2014 ${stage}`,
|
|
1783
|
+
value: stage
|
|
1784
|
+
})),
|
|
1785
|
+
new Separator(),
|
|
1786
|
+
{ name: "Skip", value: skip }
|
|
1787
|
+
]
|
|
1788
|
+
});
|
|
1789
|
+
chosen = answer === skip ? null : answer;
|
|
1790
|
+
}
|
|
1791
|
+
if (!chosen) {
|
|
1792
|
+
console.log("");
|
|
1793
|
+
console.log(
|
|
1794
|
+
chalk7.dim(
|
|
1795
|
+
` Skipped. Run ${chalk7.cyan("thinkwork login --stage <stage>")} later for an API session.`
|
|
1796
|
+
)
|
|
1797
|
+
);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
console.log("");
|
|
1801
|
+
await doCognitoLogin({
|
|
1802
|
+
stage: chosen,
|
|
1803
|
+
region: opts.region,
|
|
1804
|
+
port: opts.port,
|
|
1805
|
+
noBrowser: opts.noBrowser
|
|
1806
|
+
});
|
|
1754
1807
|
}
|
|
1755
1808
|
async function bootstrapUserAndTenant(stage, region, idToken) {
|
|
1756
1809
|
const baseUrl = getApiEndpoint(stage, region);
|
|
@@ -1879,11 +1932,10 @@ async function doApiKeyLogin(opts) {
|
|
|
1879
1932
|
}
|
|
1880
1933
|
function registerLoginCommand(program2) {
|
|
1881
1934
|
program2.command("login").description(
|
|
1882
|
-
"Sign in. Without --stage: configure AWS credentials (
|
|
1935
|
+
"Sign in. Without --stage: configure AWS credentials AND offer to sign in to a deployed stack's API (the natural one-step UX). With --stage <s>: go straight to that stack's Cognito / API login and cache a session for API-backed commands."
|
|
1883
1936
|
).option(
|
|
1884
1937
|
"--profile <name>",
|
|
1885
|
-
|
|
1886
|
-
"thinkwork"
|
|
1938
|
+
'AWS profile name to configure (used when entering new keys or SSO). Defaults to "thinkwork" only on the AWS-credentials branch; the Cognito branch leaves AWS_PROFILE alone.'
|
|
1887
1939
|
).option("--sso", "Skip the picker and go straight to SSO login").option("--keys", "Skip the picker and go straight to access-key entry").option(
|
|
1888
1940
|
"-s, --stage <name>",
|
|
1889
1941
|
"Sign in to a deployed stack instead of configuring AWS credentials"
|
|
@@ -1951,32 +2003,41 @@ Registered callback URL:
|
|
|
1951
2003
|
});
|
|
1952
2004
|
return;
|
|
1953
2005
|
}
|
|
1954
|
-
const
|
|
1955
|
-
if (!Number.isFinite(
|
|
2006
|
+
const port2 = Number.parseInt(opts.port, 10);
|
|
2007
|
+
if (!Number.isFinite(port2) || port2 < 1 || port2 > 65535) {
|
|
1956
2008
|
printError(`Invalid --port value: "${opts.port}".`);
|
|
1957
2009
|
process.exit(1);
|
|
1958
2010
|
}
|
|
1959
2011
|
await doCognitoLogin({
|
|
1960
2012
|
stage: opts.stage,
|
|
1961
2013
|
region: opts.region,
|
|
1962
|
-
port,
|
|
2014
|
+
port: port2,
|
|
1963
2015
|
noBrowser: opts.browser === false
|
|
1964
2016
|
});
|
|
1965
2017
|
return;
|
|
1966
2018
|
}
|
|
1967
|
-
|
|
2019
|
+
const targetProfile = opts.profile ?? "thinkwork";
|
|
2020
|
+
printHeader("login", targetProfile);
|
|
1968
2021
|
const awsOk = await ensureAwsCli();
|
|
1969
2022
|
if (!awsOk) process.exit(1);
|
|
2023
|
+
const port = Number.parseInt(opts.port, 10);
|
|
2024
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
2025
|
+
printError(`Invalid --port value: "${opts.port}".`);
|
|
2026
|
+
process.exit(1);
|
|
2027
|
+
}
|
|
2028
|
+
const chainOpts = { port, noBrowser: opts.browser === false };
|
|
1970
2029
|
if (opts.sso) {
|
|
1971
|
-
if (!runSsoLogin(
|
|
1972
|
-
process.env.AWS_PROFILE =
|
|
1973
|
-
finalizeAws(
|
|
2030
|
+
if (!runSsoLogin(targetProfile)) process.exit(1);
|
|
2031
|
+
process.env.AWS_PROFILE = targetProfile;
|
|
2032
|
+
const { region: region2 } = finalizeAws(targetProfile, "SSO");
|
|
2033
|
+
await offerApiLoginChain({ region: region2, ...chainOpts });
|
|
1974
2034
|
return;
|
|
1975
2035
|
}
|
|
1976
2036
|
if (opts.keys) {
|
|
1977
|
-
if (!await runKeyEntry(
|
|
1978
|
-
process.env.AWS_PROFILE =
|
|
1979
|
-
finalizeAws(
|
|
2037
|
+
if (!await runKeyEntry(targetProfile)) process.exit(1);
|
|
2038
|
+
process.env.AWS_PROFILE = targetProfile;
|
|
2039
|
+
const { region: region2 } = finalizeAws(targetProfile, "access keys");
|
|
2040
|
+
await offerApiLoginChain({ region: region2, ...chainOpts });
|
|
1980
2041
|
return;
|
|
1981
2042
|
}
|
|
1982
2043
|
const profiles = listAwsProfiles();
|
|
@@ -1986,9 +2047,10 @@ Registered callback URL:
|
|
|
1986
2047
|
console.log(
|
|
1987
2048
|
chalk7.dim(" Falling through to access-key entry for a new profile.")
|
|
1988
2049
|
);
|
|
1989
|
-
if (!await runKeyEntry(
|
|
1990
|
-
process.env.AWS_PROFILE =
|
|
1991
|
-
finalizeAws(
|
|
2050
|
+
if (!await runKeyEntry(targetProfile)) process.exit(1);
|
|
2051
|
+
process.env.AWS_PROFILE = targetProfile;
|
|
2052
|
+
const { region: region2 } = finalizeAws(targetProfile, "access keys");
|
|
2053
|
+
await offerApiLoginChain({ region: region2, ...chainOpts });
|
|
1992
2054
|
return;
|
|
1993
2055
|
}
|
|
1994
2056
|
const choice = await pickProfile(profiles);
|
|
@@ -1998,15 +2060,17 @@ Registered callback URL:
|
|
|
1998
2060
|
return;
|
|
1999
2061
|
}
|
|
2000
2062
|
if (choice.kind === "keys") {
|
|
2001
|
-
if (!await runKeyEntry(
|
|
2002
|
-
process.env.AWS_PROFILE =
|
|
2003
|
-
finalizeAws(
|
|
2063
|
+
if (!await runKeyEntry(targetProfile)) process.exit(1);
|
|
2064
|
+
process.env.AWS_PROFILE = targetProfile;
|
|
2065
|
+
const { region: region2 } = finalizeAws(targetProfile, "access keys");
|
|
2066
|
+
await offerApiLoginChain({ region: region2, ...chainOpts });
|
|
2004
2067
|
return;
|
|
2005
2068
|
}
|
|
2006
2069
|
if (choice.kind === "sso") {
|
|
2007
|
-
if (!runSsoLogin(
|
|
2008
|
-
process.env.AWS_PROFILE =
|
|
2009
|
-
finalizeAws(
|
|
2070
|
+
if (!runSsoLogin(targetProfile)) process.exit(1);
|
|
2071
|
+
process.env.AWS_PROFILE = targetProfile;
|
|
2072
|
+
const { region: region2 } = finalizeAws(targetProfile, "SSO");
|
|
2073
|
+
await offerApiLoginChain({ region: region2, ...chainOpts });
|
|
2010
2074
|
return;
|
|
2011
2075
|
}
|
|
2012
2076
|
const picked = choice.name;
|
|
@@ -2020,7 +2084,8 @@ Registered callback URL:
|
|
|
2020
2084
|
process.exit(1);
|
|
2021
2085
|
}
|
|
2022
2086
|
process.env.AWS_PROFILE = picked;
|
|
2023
|
-
finalizeAws(picked, "existing profile");
|
|
2087
|
+
const { region } = finalizeAws(picked, "existing profile");
|
|
2088
|
+
await offerApiLoginChain({ region, ...chainOpts });
|
|
2024
2089
|
}
|
|
2025
2090
|
);
|
|
2026
2091
|
}
|
|
@@ -8243,7 +8308,7 @@ async function runTenantSettingsSet(tenantArg, opts) {
|
|
|
8243
8308
|
input20.autoCloseThreadMinutes = Math.round(Number.parseFloat(opts.autoCloseAfterDays) * 60 * 24);
|
|
8244
8309
|
}
|
|
8245
8310
|
const features = parseFeatureFlags(opts.feature);
|
|
8246
|
-
if (features !== void 0) input20.features = features;
|
|
8311
|
+
if (features !== void 0) input20.features = JSON.stringify(features);
|
|
8247
8312
|
if (Object.keys(input20).length === 0) {
|
|
8248
8313
|
printError(
|
|
8249
8314
|
"Nothing to set. Pass at least one of --default-model, --monthly-budget-usd, --max-agents, --auto-close-after-days, --feature."
|
|
@@ -253,12 +253,13 @@ locals {
|
|
|
253
253
|
# the routine-approval-callback function name in the SFN execution
|
|
254
254
|
# input so the inbox_approval recipe can fanout to it on .waitForTaskToken.
|
|
255
255
|
"job-trigger" = {
|
|
256
|
-
AWS_ACCOUNT_ID
|
|
257
|
-
ROUTINE_APPROVAL_CALLBACK_FUNCTION_NAME
|
|
258
|
-
EMAIL_SEND_FUNCTION_NAME
|
|
259
|
-
ROUTINE_TASK_PYTHON_FUNCTION_NAME
|
|
260
|
-
ADMIN_OPS_MCP_FUNCTION_NAME
|
|
261
|
-
SLACK_SEND_FUNCTION_NAME
|
|
256
|
+
AWS_ACCOUNT_ID = var.account_id
|
|
257
|
+
ROUTINE_APPROVAL_CALLBACK_FUNCTION_NAME = "thinkwork-${var.stage}-api-routine-approval-callback"
|
|
258
|
+
EMAIL_SEND_FUNCTION_NAME = "thinkwork-${var.stage}-api-email-send"
|
|
259
|
+
ROUTINE_TASK_PYTHON_FUNCTION_NAME = "thinkwork-${var.stage}-api-routine-task-python"
|
|
260
|
+
ADMIN_OPS_MCP_FUNCTION_NAME = "thinkwork-${var.stage}-api-admin-ops-mcp"
|
|
261
|
+
SLACK_SEND_FUNCTION_NAME = "thinkwork-${var.stage}-api-slack-send"
|
|
262
|
+
THREAD_IDLE_MEMORY_LEARNING_FUNCTION_NAME = "thinkwork-${var.stage}-api-thread-idle-memory-learning"
|
|
262
263
|
}
|
|
263
264
|
# Phase 3 U4 Compliance outbox drainer.
|
|
264
265
|
# Connects to Aurora as the compliance_drainer role (provisioned in
|
|
@@ -307,6 +308,7 @@ resource "aws_lambda_function" "handler" {
|
|
|
307
308
|
"budgets",
|
|
308
309
|
"guardrails",
|
|
309
310
|
"scheduled-jobs",
|
|
311
|
+
"thread-idle-memory-learning",
|
|
310
312
|
"job-schedule-manager",
|
|
311
313
|
"job-trigger",
|
|
312
314
|
"routine-task-weather-email",
|
|
@@ -535,6 +535,10 @@ resource "aws_iam_role_policy" "lambda_api_cross_invoke" {
|
|
|
535
535
|
# decision. Calls SendTaskSuccess/SendTaskFailure on the SFN
|
|
536
536
|
# task token; idempotent on already-consumed tokens.
|
|
537
537
|
"arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-routine-resume",
|
|
538
|
+
# thread-idle-memory-learning: job-trigger invokes this
|
|
539
|
+
# RequestResponse after a 15-minute requester-thread idle timer
|
|
540
|
+
# passes its stale guard.
|
|
541
|
+
"arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-thread-idle-memory-learning",
|
|
538
542
|
# workspace-files-efs: workspace-files invokes this (RequestResponse)
|
|
539
543
|
# for Computer-target list/get to bypass the computer_tasks queue
|
|
540
544
|
# and read EFS directly. Standalone resource below.
|
|
@@ -212,63 +212,33 @@ type Mutation {
|
|
|
212
212
|
|
|
213
213
|
type Subscription {
|
|
214
214
|
_empty: String
|
|
215
|
-
onAgentStatusChanged(tenantId: ID!): AgentStatusEvent
|
|
216
|
-
@aws_api_key
|
|
217
|
-
@aws_cognito_user_pools
|
|
218
|
-
@aws_iam
|
|
215
|
+
onAgentStatusChanged(tenantId: ID!): AgentStatusEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
219
216
|
@aws_subscribe(mutations: ["notifyAgentStatus"])
|
|
220
217
|
|
|
221
|
-
onNewMessage(threadId: ID!): NewMessageEvent
|
|
222
|
-
@aws_api_key
|
|
223
|
-
@aws_cognito_user_pools
|
|
224
|
-
@aws_iam
|
|
218
|
+
onNewMessage(threadId: ID!): NewMessageEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
225
219
|
@aws_subscribe(mutations: ["notifyNewMessage"])
|
|
226
220
|
|
|
227
|
-
onHeartbeatActivity(tenantId: ID!): HeartbeatActivityEvent
|
|
228
|
-
@aws_api_key
|
|
229
|
-
@aws_cognito_user_pools
|
|
230
|
-
@aws_iam
|
|
221
|
+
onHeartbeatActivity(tenantId: ID!): HeartbeatActivityEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
231
222
|
@aws_subscribe(mutations: ["notifyHeartbeatActivity"])
|
|
232
223
|
|
|
233
|
-
onThreadUpdated(tenantId: ID!): ThreadUpdateEvent
|
|
234
|
-
@aws_api_key
|
|
235
|
-
@aws_cognito_user_pools
|
|
236
|
-
@aws_iam
|
|
224
|
+
onThreadUpdated(tenantId: ID!): ThreadUpdateEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
237
225
|
@aws_subscribe(mutations: ["notifyThreadUpdate"])
|
|
238
226
|
|
|
239
|
-
onInboxItemStatusChanged(tenantId: ID!): InboxItemStatusEvent
|
|
240
|
-
@aws_api_key
|
|
241
|
-
@aws_cognito_user_pools
|
|
242
|
-
@aws_iam
|
|
227
|
+
onInboxItemStatusChanged(tenantId: ID!): InboxItemStatusEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
243
228
|
@aws_subscribe(mutations: ["notifyInboxItemUpdate"])
|
|
244
229
|
|
|
245
|
-
onThreadTurnUpdated(tenantId: ID!): ThreadTurnUpdateEvent
|
|
246
|
-
@aws_api_key
|
|
247
|
-
@aws_cognito_user_pools
|
|
248
|
-
@aws_iam
|
|
230
|
+
onThreadTurnUpdated(tenantId: ID!): ThreadTurnUpdateEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
249
231
|
@aws_subscribe(mutations: ["notifyThreadTurnUpdate"])
|
|
250
232
|
|
|
251
|
-
onComputerThreadChunk(threadId: ID!): ComputerThreadChunkEvent
|
|
252
|
-
@aws_api_key
|
|
253
|
-
@aws_cognito_user_pools
|
|
254
|
-
@aws_iam
|
|
233
|
+
onComputerThreadChunk(threadId: ID!): ComputerThreadChunkEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
255
234
|
@aws_subscribe(mutations: ["publishComputerThreadChunk"])
|
|
256
235
|
|
|
257
|
-
onOrgUpdated(tenantId: ID!): OrgUpdateEvent
|
|
258
|
-
@aws_api_key
|
|
259
|
-
@aws_cognito_user_pools
|
|
260
|
-
@aws_iam
|
|
236
|
+
onOrgUpdated(tenantId: ID!): OrgUpdateEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
261
237
|
@aws_subscribe(mutations: ["notifyOrgUpdate"])
|
|
262
238
|
|
|
263
|
-
onCostRecorded(tenantId: ID!): CostRecordedEvent
|
|
264
|
-
@aws_api_key
|
|
265
|
-
@aws_cognito_user_pools
|
|
266
|
-
@aws_iam
|
|
239
|
+
onCostRecorded(tenantId: ID!): CostRecordedEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
267
240
|
@aws_subscribe(mutations: ["notifyCostRecorded"])
|
|
268
241
|
|
|
269
|
-
onEvalRunUpdated(tenantId: ID!): EvalRunUpdateEvent
|
|
270
|
-
@aws_api_key
|
|
271
|
-
@aws_cognito_user_pools
|
|
272
|
-
@aws_iam
|
|
242
|
+
onEvalRunUpdated(tenantId: ID!): EvalRunUpdateEvent @aws_api_key @aws_cognito_user_pools @aws_iam
|
|
273
243
|
@aws_subscribe(mutations: ["notifyEvalRunUpdate"])
|
|
274
244
|
}
|