@platless/axom-cli 0.1.0 → 0.3.13

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.
Files changed (109) hide show
  1. package/README.md +1 -1
  2. package/dist/authScopes.js +19 -0
  3. package/dist/axom-patch/cli/README.md +110 -0
  4. package/dist/axom-patch/cli/pi-steering.json +37 -0
  5. package/dist/axom-patch/cli/probes/claude-late-async-terminal.mjs +34 -0
  6. package/dist/axom-patch/cli/probes/pi-steering.mjs +80 -0
  7. package/dist/axom-patch/cli/probes/qoder-steering.mjs +89 -0
  8. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +49 -0
  9. package/dist/axom-patch/cli/qoder-background-terminal.md +81 -0
  10. package/dist/axom-patch/cli/qoder-foreground-output.md +41 -0
  11. package/dist/axom-patch/cli/qoder-steering.json +52 -0
  12. package/dist/axom-patch/cli/qoder-steering.md +30 -0
  13. package/dist/axom-patch/cli/qoder-subagent-origin.json +73 -0
  14. package/dist/axom-patch/cli/qoder-subagent-subwork.md +73 -0
  15. package/dist/axom-patch/cli/runtime.json +37 -0
  16. package/dist/axom-patch/cli/steering/claude-steering-js.patch +89 -0
  17. package/dist/axom-patch/cli/steering/claude-steering-types.patch +28 -0
  18. package/dist/axom-patch/cli/steering/codex-steering.patch +57 -0
  19. package/dist/axom-patch/cli/steering/pi-steering.js +79 -0
  20. package/dist/cli.js +9 -1
  21. package/dist/commands/auth.js +4 -13
  22. package/dist/commands/execute.js +4 -4
  23. package/dist/commands/record.js +6 -6
  24. package/dist/commands/struct.js +4 -4
  25. package/dist/commands/workstation.js +2 -2
  26. package/dist/credentials.js +2 -2
  27. package/dist/install/claude/commands.js +13 -13
  28. package/dist/mcp.js +32 -14
  29. package/dist/workstation/acp/acpAgents.js +447 -166
  30. package/dist/workstation/acp/acpClient.js +232 -18
  31. package/dist/workstation/acp/acpSession.js +502 -74
  32. package/dist/workstation/acp/activityBusy.js +43 -0
  33. package/dist/workstation/acp/activityReporting.js +54 -0
  34. package/dist/workstation/acp/adoption.js +107 -49
  35. package/dist/workstation/acp/authUrlSniffer.js +36 -0
  36. package/dist/workstation/acp/contract.js +11 -6
  37. package/dist/workstation/acp/delegationMcp.js +24 -0
  38. package/dist/workstation/acp/draftInterceptor.js +9 -0
  39. package/dist/workstation/acp/managedPatches.js +228 -0
  40. package/dist/workstation/acp/patchAssets.js +63 -0
  41. package/dist/workstation/acp/permission.js +300 -34
  42. package/dist/workstation/acp/qoderStartup.js +45 -0
  43. package/dist/workstation/acp/registry/catalog.js +263 -4
  44. package/dist/workstation/acp/registry/detect.js +55 -0
  45. package/dist/workstation/acp/sessionScope.js +19 -0
  46. package/dist/workstation/acp/sessionUpdateMapper.js +39 -21
  47. package/dist/workstation/acp/terminalLogin.js +197 -0
  48. package/dist/workstation/acp/test/fakeAgent.js +185 -9
  49. package/dist/workstation/acp/validate.js +33 -5
  50. package/dist/workstation/agentSessions.js +532 -44
  51. package/dist/workstation/channels/agent.js +7 -1
  52. package/dist/workstation/channels/computer.js +152 -4
  53. package/dist/workstation/channels/management.js +3 -3
  54. package/dist/workstation/config.js +3 -1
  55. package/dist/workstation/connection/encryptedChannel.js +88 -21
  56. package/dist/workstation/daemon.js +25 -3
  57. package/dist/workstation/desktop/binding.js +36 -0
  58. package/dist/workstation/desktop/bridge.js +212 -0
  59. package/dist/workstation/desktop/flow-control.js +52 -0
  60. package/dist/workstation/desktop/focused-text.js +130 -0
  61. package/dist/workstation/desktop/protocol.js +35 -0
  62. package/dist/workstation/fileOps.js +277 -0
  63. package/dist/workstation/filePaths.js +174 -0
  64. package/dist/workstation/fileWorker.js +81 -0
  65. package/dist/workstation/fileWorkerClient.js +121 -0
  66. package/dist/workstation/oauth/deviceAuthorization.js +21 -12
  67. package/dist/workstation/protocol/agent.js +154 -5
  68. package/dist/workstation/protocol/common.js +6 -0
  69. package/dist/workstation/protocol/computer.js +92 -3
  70. package/dist/workstation/qoderAdapter/backgroundProcess.js +106 -0
  71. package/dist/workstation/qoderAdapter/backgroundTerminal.js +262 -0
  72. package/dist/workstation/qoderAdapter/completedContinuation.js +139 -0
  73. package/dist/workstation/qoderAdapter/contract.js +78 -0
  74. package/dist/workstation/qoderAdapter/foregroundOutput.js +30 -0
  75. package/dist/workstation/qoderAdapter/history.js +87 -0
  76. package/dist/workstation/qoderAdapter/identityIndex.js +191 -0
  77. package/dist/workstation/qoderAdapter/inputQueue.js +42 -0
  78. package/dist/workstation/qoderAdapter/main.js +93 -0
  79. package/dist/workstation/qoderAdapter/mapper.js +117 -0
  80. package/dist/workstation/qoderAdapter/observerPlugin.js +245 -0
  81. package/dist/workstation/qoderAdapter/observerReceiver.js +187 -0
  82. package/dist/workstation/qoderAdapter/permissions.js +61 -0
  83. package/dist/workstation/qoderAdapter/profile.js +93 -0
  84. package/dist/workstation/qoderAdapter/sdkDriver.js +49 -0
  85. package/dist/workstation/qoderAdapter/session.js +319 -0
  86. package/dist/workstation/qoderAdapter/subagentMeta.js +28 -0
  87. package/dist/workstation/qoderAdapter/subwork.js +77 -0
  88. package/dist/workstation/qoderAdapter/taskControl.js +38 -0
  89. package/dist/workstation/qoderAdapter/taskEvent.js +26 -0
  90. package/dist/workstation/qoderAdapter/taskLedger.js +163 -0
  91. package/dist/workstation/qoderAdapter/types.js +1 -0
  92. package/dist/workstation/qoderAdapter/usage.js +37 -0
  93. package/dist/workstation/register.js +4 -0
  94. package/dist/workstation/tools/browser.js +394 -0
  95. package/dist/workstation/tools/client.js +269 -0
  96. package/dist/workstation/tools/core.js +50 -0
  97. package/dist/workstation/tools/files.js +525 -0
  98. package/dist/workstation/tools/keeperProcess.js +114 -0
  99. package/dist/workstation/tools/terminal.js +214 -0
  100. package/dist/workstation/tools/validation.js +50 -0
  101. package/dist/workstation/update/coordinator.js +29 -0
  102. package/dist/workstation/update/npmInstaller.js +2 -0
  103. package/dist/workstation/update/ptyPreflight.js +24 -0
  104. package/dist/workstation/update/taskRegistry.js +44 -20
  105. package/dist/workstation/update/watchdog.js +66 -0
  106. package/package.json +14 -4
  107. package/skills/axom/SKILL.md +55 -17
  108. package/skills/axom-definition/SKILL.md +32 -32
  109. package/skills/axom-platform/SKILL.md +39 -39
@@ -54,8 +54,8 @@ async function ensureRegistered(deps, serverFlag, nameFlag) {
54
54
  * that runs in `/workspace` — a container is precisely where this product runs untrusted code. What
55
55
  * would leak is not the workstation's own key pair (scoped to one workstation, on disk) but a
56
56
  * credential that authenticates *as the user*: it can register further workstations under their
57
- * account and, with the `mcp` scope, call every MCP tool as them, until somebody notices and revokes
58
- * it. Fixed once here rather than filtered at each of the two spawn sites, so the next spawn site
57
+ * account and, with business scopes, call granted MCP tools as them, until somebody notices and
58
+ * revokes it. Fixed once here rather than filtered at each of the two spawn sites, so the next spawn site
59
59
  * inherits the fix instead of having to remember it.
60
60
  *
61
61
  * Nothing after this point needs the value. The daemon authenticates with the file credential
@@ -168,8 +168,8 @@ async function refreshRequest(server, refreshToken) {
168
168
  }),
169
169
  });
170
170
  }
171
- export async function revokeRefreshToken(server, refreshToken) {
172
- await fetch(`${normalizedServer(server)}/oauth/revoke`, {
171
+ export async function revokeRefreshToken(server, refreshToken, fetch_ = globalThis.fetch) {
172
+ await fetch_(`${normalizedServer(server)}/oauth/revoke`, {
173
173
  method: 'POST',
174
174
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
175
175
  body: new URLSearchParams({ token: refreshToken, token_type_hint: 'refresh_token', client_id: 'axom-cli' }),
@@ -8,8 +8,8 @@ Use the Axomwork MCP tools to answer this structural question about the platform
8
8
  $ARGUMENTS
9
9
 
10
10
  Workflow:
11
- 1. \`axom__struct__list_applications\` to see applications; \`axom__struct__get_application\` for one app's models and menus.
12
- 2. \`axom__struct__list_models\` / \`axom__struct__get_model\` for a model's attributes, operations, and display metadata.
11
+ 1. \`axom__app_struct__list_applications\` to see applications; \`axom__app_struct__get_application\` for one app's models and menus.
12
+ 2. \`axom__app_struct__list_models\` / \`axom__app_struct__get_model\` for a model's attributes, operations, and display metadata.
13
13
  3. Prefer read-only struct tools here; do not mutate anything from this command.
14
14
 
15
15
  For the underlying concepts (applications, models, attributes, references), load guide \`concepts/data-model/index.md\` with \`axom__guide__load\`.
@@ -23,10 +23,10 @@ Use the Axomwork MCP record tools to fulfil this request:
23
23
  $ARGUMENTS
24
24
 
25
25
  Workflow:
26
- 1. Discover structure first when unsure: \`axom__struct__get_model\` for attribute names and types.
27
- 2. Query with \`axom__record__query\` (filters/sorts/page criteria — for criteria syntax, load guide \`agent-integration/mcp/record-query-criteria.md\`).
28
- 3. Mutate with \`axom__record__create\` / \`axom__record__update\` / \`axom__record__upsert\` / \`axom__record__delete\`. Read the record first before updating; confirm with the user before deleting.
29
- 4. \`axom__record__upsert\` needs \`identityField\` and the identity value inside \`record\`.
26
+ 1. Discover structure first when unsure: \`axom__app_struct__get_model\` for attribute names and types.
27
+ 2. Query with \`axom__record_query__query_records\` (filters/sorts/page criteria — for criteria syntax, load guide \`agent-integration/mcp/record-query-criteria.md\`).
28
+ 3. Mutate with \`axom__record_write__create_record\` / \`axom__record_write__update_record\` / \`axom__record_write__upsert_record\` / \`axom__record_write__delete_record\`. Read the record first before updating; confirm with the user before deleting.
29
+ 4. \`axom__record_write__upsert_record\` needs \`identityField\` and the identity value inside \`record\`.
30
30
 
31
31
  For safe mutation patterns, load guide \`agent-integration/operating-patterns/safe-record-mutations.md\`.
32
32
  `,
@@ -39,18 +39,18 @@ Execute the requested Axomwork operation or service:
39
39
  $ARGUMENTS
40
40
 
41
41
  Workflow:
42
- 1. Find the operation on its model (\`axom__struct__get_model\`) or the service by name.
43
- 2. Call \`axom__operation__execute\` (applicationName/modelName/operationName/input) or \`axom__service__execute\` (serviceName/input).
42
+ 1. Find the operation on its model (\`axom__app_struct__get_model\`) or the service by name.
43
+ 2. Call \`axom__execute__run_operation\` (applicationName/modelName/operationName/input) or \`axom__execute__run_service\` (serviceName/input).
44
44
  3. Interpret the ExecuteResult payload: \`status\` (success/failure/running), \`message\`, \`text\`, \`returnValue\`, \`runId\`/\`runType\`, \`errors[]\` ({target,code,message}).
45
45
  4. When \`status\` is \`running\`, follow \`runId\` (see \`runType\`) until a terminal state before reporting the outcome.
46
46
 
47
47
  For sync-vs-job semantics load guide \`concepts/execution/synchronous-and-task-execution.md\`; for run following load guide \`agent-integration/operating-patterns/following-runs.md\`.
48
48
  `,
49
49
  definition: `---
50
- description: Develop Axomwork definitions — apps, models, attributes, operations, workflows (requires ADMIN)
50
+ description: Develop Axomwork definitions — apps, models, attributes, operations, workflows (requires app_studio scope and an admin account)
51
51
  ---
52
52
 
53
- You are editing Axomwork application definitions with the canonical \`axom__definition__*\` MCP tools (ADMIN token required).
53
+ You are editing Axomwork application definitions with the canonical \`axom__app_studio__*\` MCP tools (requires the \`app_studio\` scope and an administrator account).
54
54
 
55
55
  $ARGUMENTS
56
56
 
@@ -61,16 +61,16 @@ Rules:
61
61
  4. Confirm destructive definition deletes with the user first.
62
62
  `,
63
63
  platform: `---
64
- description: Operate Axomwork platform configuration — AI vendors/profiles, cowork agents, datasources, platform skills and settings (requires ADMIN)
64
+ description: Operate Axomwork platform configuration — AI providers/profiles, cowork agents, datasources, platform skills and settings (requires setting_platform scope and an admin account)
65
65
  ---
66
66
 
67
- You are operating Axomwork platform configuration with the canonical \`axom__platform__*\` MCP tools (skills, settings, AI vendors/profiles/memory, cowork agents, data sources; ADMIN token required).
67
+ You are operating Axomwork platform configuration with the canonical \`axom__setting_platform__*\` MCP tools (skills, settings, AI providers/profiles/memory, cowork agents, data sources; requires the \`setting_platform\` scope and an administrator account).
68
68
 
69
69
  $ARGUMENTS
70
70
 
71
71
  Rules:
72
72
  1. Read current configuration before editing; respect ETag guards where tools expose them.
73
- 2. For configuration semantics (AI vendors and profiles, cowork agents, datasources, platform skills and settings), load guide \`platform-operations/index.md\` first.
73
+ 2. For configuration semantics (AI providers and profiles, cowork agents, datasources, platform skills and settings), load guide \`platform-operations/index.md\` first.
74
74
  3. Confirm with the user before changes that can interrupt running workflows or agent sessions.
75
75
  `,
76
76
  guide: `---
package/dist/mcp.js CHANGED
@@ -11,7 +11,7 @@ export async function callAxomTool(server, toolName, args) {
11
11
  }
12
12
  catch (error) {
13
13
  if (isInsufficientScope(error))
14
- throw insufficientScopeError(normalizedServer);
14
+ throw insufficientScopeError(normalizedServer, toolName);
15
15
  if (!isUnauthorized(error))
16
16
  throw error;
17
17
  // An env token cannot be refreshed: `accessTokenFor` returns the same literal whatever
@@ -28,7 +28,7 @@ export async function callAxomTool(server, toolName, args) {
28
28
  }
29
29
  catch (error) {
30
30
  if (isInsufficientScope(error))
31
- throw insufficientScopeError(normalizedServer);
31
+ throw insufficientScopeError(normalizedServer, toolName);
32
32
  if (isUnauthorized(error)) {
33
33
  throw new AuthError(`MCP authentication failed with ${normalizedServer}. Run: axom auth --server ${normalizedServer}`);
34
34
  }
@@ -45,7 +45,7 @@ async function callAxomToolOnce(server, token, toolName, args) {
45
45
  try {
46
46
  await client.connect(transport);
47
47
  const result = await client.callTool({ name: toolName, arguments: args });
48
- return toToolOutcome(result);
48
+ return addPermissionAdvice(toolName, toToolOutcome(result));
49
49
  }
50
50
  finally {
51
51
  try {
@@ -89,12 +89,7 @@ function isUnauthorized(error) {
89
89
  const message = error instanceof Error ? error.message : String(error);
90
90
  return /\b401\b/.test(message);
91
91
  }
92
- /**
93
- * `/mcp` returns 403 (not 401) when the bearer token is valid but lacks the `mcp` scope — this is
94
- * how a `workstation`-only access token (the default for a token minted without `--scope mcp`)
95
- * looks from the client. Retrying with a refreshed token would not help, since the new token would
96
- * carry the same scopes, so this is checked before the 401/refresh path, not folded into it.
97
- */
92
+ /** A 403 is an authorization refusal, so refreshing the same grant cannot make it succeed. */
98
93
  export function isInsufficientScope(error) {
99
94
  const candidate = error;
100
95
  if (candidate.status === 403 || candidate.code === 403 || candidate.response?.status === 403)
@@ -102,11 +97,34 @@ export function isInsufficientScope(error) {
102
97
  const message = error instanceof Error ? error.message : String(error);
103
98
  return /\b403\b/.test(message);
104
99
  }
105
- export function insufficientScopeError(server) {
106
- const hint = usesEnvAccessToken()
107
- ? ' Set AXOM_ACCESS_TOKEN to a token that includes the "mcp" scope, or run axom auth.'
108
- : '';
109
- return new AuthError(`MCP token is missing the "mcp" scope for ${server}.${hint}`);
100
+ /** Abilities every credential has (Ability registry: BASELINE with the MCP surface). */
101
+ const BASELINE_TOOL_SCOPES = new Set(['system', 'guide', 'app_struct']);
102
+ const TOOL_SCOPE = /^axom__([a-z][a-z0-9]*(?:_[a-z0-9]+)*)__[a-z]/;
103
+ const ADMIN_NOTE = ' Scopes marked "Requires an administrator account" on the consent page also need an administrator user.';
104
+ /** An external tool's middle segment IS the OAuth scope that grants it (spec 2026-09-26 §3.5). */
105
+ export function scopeForTool(toolName) {
106
+ const scope = toolName ? TOOL_SCOPE.exec(toolName)?.[1] : undefined;
107
+ return scope && !BASELINE_TOOL_SCOPES.has(scope) ? scope : undefined;
108
+ }
109
+ export function insufficientScopeError(server, toolName) {
110
+ const scope = scopeForTool(toolName);
111
+ if (!scope)
112
+ return new AuthError(`MCP authorization was denied by ${server}. Check this credential's approved scopes.`);
113
+ const remedy = usesEnvAccessToken()
114
+ ? ` Create or edit an access token under Settings → Security and grant ${scope}.`
115
+ : ` Run axom auth --server ${server} and select ${scope}.`;
116
+ return new AuthError(`MCP authorization was denied by ${server}. Required OAuth scope: ${scope}.${remedy}${ADMIN_NOTE}`);
117
+ }
118
+ function addPermissionAdvice(toolName, outcome) {
119
+ if (!outcome.isError || !isPermissionFailure(outcome.text))
120
+ return outcome;
121
+ const scope = scopeForTool(toolName);
122
+ if (!scope)
123
+ return outcome;
124
+ return { ...outcome, text: `${outcome.text}\nRequired OAuth scope: ${scope}.${ADMIN_NOTE}` };
125
+ }
126
+ function isPermissionFailure(message) {
127
+ return /permission denied|not authorized|forbidden|insufficient[_ ]scope|MCP grant is no longer valid/i.test(message);
110
128
  }
111
129
  /**
112
130
  * Is this process authenticating with `AXOM_ACCESS_TOKEN` rather than a stored login? Mirrors