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
- console.log("");
1748
- console.log(
1749
- ` ${chalk7.bold("Next:")} run ${chalk7.cyan("thinkwork login --stage <stage>")} if you also need`
1750
- );
1751
- console.log(
1752
- ` an API session (required for ${chalk7.cyan("eval")}, ${chalk7.cyan("agent")}, ${chalk7.cyan("thread")}, etc.).`
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 (for deploy / destroy). With --stage <s>: sign in to that stack's Cognito / API and cache a session for API-backed commands."
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
- "AWS profile name to configure (used when entering new keys or SSO)",
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 port = Number.parseInt(opts.port, 10);
1955
- if (!Number.isFinite(port) || port < 1 || port > 65535) {
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
- printHeader("login", opts.profile);
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(opts.profile)) process.exit(1);
1972
- process.env.AWS_PROFILE = opts.profile;
1973
- finalizeAws(opts.profile, "SSO");
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(opts.profile)) process.exit(1);
1978
- process.env.AWS_PROFILE = opts.profile;
1979
- finalizeAws(opts.profile, "access keys");
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(opts.profile)) process.exit(1);
1990
- process.env.AWS_PROFILE = opts.profile;
1991
- finalizeAws(opts.profile, "access keys");
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(opts.profile)) process.exit(1);
2002
- process.env.AWS_PROFILE = opts.profile;
2003
- finalizeAws(opts.profile, "access keys");
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(opts.profile)) process.exit(1);
2008
- process.env.AWS_PROFILE = opts.profile;
2009
- finalizeAws(opts.profile, "SSO");
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 = 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"
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkwork-cli",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Thinkwork CLI — deploy, manage, and interact with your Thinkwork stack",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",