@chalksurf/cli 0.2.3 → 0.2.4

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/docs/agents.md CHANGED
@@ -30,11 +30,16 @@ Prefer storing the token in the explicit agent profile for repeated Codex/CI run
30
30
 
31
31
  ## Command Contract
32
32
 
33
- For agent-driven imports, always use:
33
+ For agent-driven workflows, always use:
34
+
35
+ - `--json` so the response stays machine-readable
36
+ - an explicit `--profile ENV-AGENT_TYPE`
37
+ - an explicit selected organization through the profile, `--organization`, or `CHALKSURF_ORGANIZATION_ID`
38
+
39
+ For agent-driven imports, also use:
34
40
 
35
41
  - `--manifest -` or a generated manifest file
36
42
  - `--wait` when the next step depends on parsed content being saved
37
- - `--json` so the response stays machine-readable
38
43
 
39
44
  Recommended invocation shape:
40
45
 
@@ -51,6 +56,121 @@ In `--json` mode:
51
56
  Wait-style failures still include a populated `result` payload, so agents can inspect partial outcomes on exit code `6` or `7`.
52
57
  When an import requests translations, `--wait` waits for the import job only. Follow-up translation jobs are listed under each completed import job's `translationJobs`.
53
58
 
59
+ ## Shared CLI/MCP Primitive Contract
60
+
61
+ The CLI and MCP surface are backed by the same agent resource contracts in `packages/shared/src/agent-tools`. Prefer these primitives over UI-only assumptions or direct database access:
62
+
63
+ - Read tools return complete resource details or focused repair queues.
64
+ - Write tools return a `receipt` with `resource`, `operation`, `changedPaths`, `precondition`, `warnings`, and `details`.
65
+ - Patch-style writes require `expectedUpdatedAt` from a fresh `get` call.
66
+ - Exercise updates require shared-usage care. Use `exercise usage` first when the exercise might appear outside the target sheet.
67
+ - Validate proposed LaTeX with `exercise validate-latex` before applying content updates.
68
+ - No delete or transfer-between-organization agent primitive is intentionally exposed.
69
+
70
+ Write receipts are the agent audit log. Preserve them in run notes or user-visible summaries when the user needs to double-check what changed.
71
+
72
+ ## CLI Resource Primitives
73
+
74
+ Use these commands after authentication and organization selection:
75
+
76
+ | Goal | CLI command |
77
+ | --- | --- |
78
+ | Fetch one sheet | `chalksurf sheet get SHEET_ID --json` |
79
+ | Get a sheet repair queue | `chalksurf sheet issues SHEET_ID --json` |
80
+ | Copy a sheet | `chalksurf sheet copy SHEET_ID "New name" --exercise-copy-mode keep_references --json` |
81
+ | Create an editable variant sheet | `chalksurf sheet copy SHEET_ID "Variant name" --exercise-copy-mode copy_all_exercises --json` |
82
+ | Append existing exercises to a sheet | `chalksurf sheet append SHEET_ID EXERCISE_ID... --json` |
83
+ | Patch sheet metadata | `chalksurf sheet update SHEET_ID --expected-updated-at ISO --patch-json JSON --json` |
84
+ | List folders | `chalksurf sheet folder list --json` |
85
+ | Create a folder | `chalksurf sheet folder create "Folder name" --parent-id FOLDER_ID --json` |
86
+ | Rename a folder | `chalksurf sheet folder rename FOLDER_ID "New name" --json` |
87
+ | Fetch one exercise | `chalksurf exercise get EXERCISE_ID --json` |
88
+ | Inspect exercise usage | `chalksurf exercise usage EXERCISE_ID --json` |
89
+ | Copy one exercise | `chalksurf exercise copy EXERCISE_ID --json` |
90
+ | Patch exercise fields | `chalksurf exercise update EXERCISE_ID --expected-updated-at ISO --patch-json JSON --json` |
91
+ | Validate LaTeX snippets | `chalksurf exercise validate-latex --snippet "path=value" --json` |
92
+
93
+ When appending private exercises to a public sheet, the CLI requires `--confirm-make-exercises-public` because the append makes those exercises public.
94
+
95
+ ## Repair Workflow: Fix LaTeX Errors In A Sheet
96
+
97
+ 1. Fetch the sheet and issue queue:
98
+
99
+ ```bash
100
+ chalksurf --profile prod-codex sheet get SHEET_ID --json
101
+ chalksurf --profile prod-codex sheet issues SHEET_ID --json
102
+ ```
103
+
104
+ 2. For each `invalid_latex` issue, use `patchPath` to identify the field to edit. The issue also includes the current validation details.
105
+ 3. Validate proposed replacements before writing:
106
+
107
+ ```bash
108
+ chalksurf --profile prod-codex exercise validate-latex \
109
+ --snippet "translations.english.exercise_text=Find $x^2$." \
110
+ --json
111
+ ```
112
+
113
+ 4. Patch the owning exercise or sheet with the `updated_at` value from the latest `get` result:
114
+
115
+ ```bash
116
+ chalksurf --profile prod-codex exercise update EXERCISE_ID \
117
+ --target-sheet-id SHEET_ID \
118
+ --expected-updated-at 2026-01-01T00:00:00.000Z \
119
+ --patch-json '{"translations":{"english":{"exercise_text":"Find $x^2$."}}}' \
120
+ --json
121
+ ```
122
+
123
+ 5. Re-run `sheet issues SHEET_ID --json` until the invalid LaTeX count is zero or the remaining items need human judgment.
124
+
125
+ Do not pass `--allow-shared-exercise-update` unless the user has acknowledged that the same exercise is used outside the target sheet.
126
+
127
+ ## Variant Workflow: Create A Slightly Easier Sheet
128
+
129
+ Use `copy_all_exercises` when the target is an editable variant. Shared public exercises often cannot be edited by the selected organization, so keeping references usually blocks variant edits.
130
+
131
+ ```bash
132
+ chalksurf --profile prod-codex sheet copy SHEET_ID "Easier variant" \
133
+ --exercise-copy-mode copy_all_exercises \
134
+ --json
135
+ ```
136
+
137
+ The copy receipt includes `details.exerciseIdMap`, with one row per source exercise and the copied exercise ID when a copy was made. Use that mapping to update the copied exercises, then re-run `sheet get` and `sheet issues` against the copied sheet.
138
+
139
+ Use `keep_references` only when the user wants a new sheet arrangement that still points at the original exercises.
140
+
141
+ ## MCP Parity
142
+
143
+ MCP clients should use tool discovery from the client. The ChalkSurf MCP endpoint exposes the same resource primitives under snake_case names:
144
+
145
+ | CLI command | MCP tool |
146
+ | --- | --- |
147
+ | `auth status` | `get_auth_status` |
148
+ | `org list` | `list_organizations` |
149
+ | `exercise search` | `search_exercises` |
150
+ | `exercise get` | `get_exercise` |
151
+ | `exercise usage` | `get_exercise_usage` |
152
+ | `exercise copy` | `copy_exercise` |
153
+ | `exercise update` | `update_exercise` |
154
+ | `exercise validate-latex` | `validate_latex_snippets` |
155
+ | `exercise import` | `import_exercise` |
156
+ | `exercise import-solution` | `import_exercise_solution` |
157
+ | `sheet search` | `search_sheets` |
158
+ | `sheet get` | `get_sheet` |
159
+ | `sheet issues` | `get_sheet_issues` |
160
+ | `sheet copy` | `copy_sheet` |
161
+ | `sheet append` | `append_exercises_to_sheet` |
162
+ | `sheet update` | `update_sheet` |
163
+ | `sheet import` | `import_sheet` |
164
+ | `sheet import-solutions` | `import_sheet_solutions` |
165
+ | `sheet folder list` | `list_folders` |
166
+ | `sheet folder create` | `create_folder` |
167
+ | `sheet folder rename` | `rename_folder` |
168
+ | `job list` | `list_jobs` |
169
+ | `job get` | `get_job` |
170
+ | `job wait` | `wait_for_jobs` |
171
+
172
+ MCP calls authenticate through ChalkSurf OAuth, not CLI tokens. Read tools require `chalksurf:read`; write tools require `chalksurf:write` plus a write grant for the requested `organizationId`. Each organization-scoped MCP call must pass `organizationId`.
173
+
54
174
  ## Manifest Design
55
175
 
56
176
  Prefer manifests over large positional argument lists.
@@ -69,7 +189,7 @@ Use `sourceId` whenever a browsing step or upstream scraper already has a stable
69
189
 
70
190
  ```json
71
191
  {
72
- "organizationId": "org_123",
192
+ "organizationId": "00000000-0000-4000-8000-000000000001",
73
193
  "wait": true,
74
194
  "sheets": [
75
195
  {
package/docs/manual.md CHANGED
@@ -184,6 +184,48 @@ Search public resources instead:
184
184
  chalksurf sheet search --text "OKTV" --ownership public
185
185
  ```
186
186
 
187
+ ## Inspecting And Editing Resources
188
+
189
+ Fetch full sheet or exercise details:
190
+
191
+ ```bash
192
+ chalksurf sheet get 00000000-0000-4000-8000-000000000001 --json
193
+ chalksurf exercise get 00000000-0000-4000-8000-000000000002 --json
194
+ ```
195
+
196
+ List sheet issues that are useful for repair workflows:
197
+
198
+ ```bash
199
+ chalksurf sheet issues 00000000-0000-4000-8000-000000000001 --json
200
+ ```
201
+
202
+ Copy a sheet for a variant workflow. Use `copy_all_exercises` when you want editable exercise copies:
203
+
204
+ ```bash
205
+ chalksurf sheet copy 00000000-0000-4000-8000-000000000001 "Easier variant" \
206
+ --exercise-copy-mode copy_all_exercises \
207
+ --json
208
+ ```
209
+
210
+ Patch an exercise with an optimistic update precondition:
211
+
212
+ ```bash
213
+ chalksurf exercise update 00000000-0000-4000-8000-000000000002 \
214
+ --expected-updated-at 2026-01-01T00:00:00.000Z \
215
+ --patch-json '{"translations":{"english":{"exercise_text":"Find $x^2$."}}}' \
216
+ --json
217
+ ```
218
+
219
+ Write commands return a receipt with the affected resource, changed paths, warnings, and operation-specific details. Fetch the resource first and use its current `updated_at` value for patch commands.
220
+
221
+ Manage folders:
222
+
223
+ ```bash
224
+ chalksurf sheet folder list --json
225
+ chalksurf sheet folder create "Practice Sets" --json
226
+ chalksurf sheet folder rename 00000000-0000-4000-8000-000000000003 "Variant Sheets" --json
227
+ ```
228
+
187
229
  ## Troubleshooting
188
230
 
189
231
  Missing auth:
@@ -209,5 +251,6 @@ chalksurf job wait job_123 --json
209
251
  Further reference:
210
252
 
211
253
  - [Agent and Codex guide](./agents.md)
254
+ - [MCP guide](./mcp.md)
212
255
  - [Manifest reference](./manifest.md)
213
256
  - [Exit codes and JSON errors](./exit-codes.md)
package/docs/mcp.md ADDED
@@ -0,0 +1,88 @@
1
+ # MCP Guide
2
+
3
+ ChalkSurf exposes a remote MCP endpoint for agent workflows that need the same capabilities as the CLI without shelling out to `chalksurf`.
4
+
5
+ ## Endpoint And Auth
6
+
7
+ - Endpoint: `/mcp` on the ChalkSurf API host.
8
+ - Authentication: OAuth access tokens issued through the ChalkSurf MCP authorization flow.
9
+ - CLI tokens and browser session tokens are rejected on `/mcp`.
10
+ - Read tools require the `chalksurf:read` scope.
11
+ - Write tools require the `chalksurf:write` scope and a write grant for the requested organization.
12
+ - Organization-scoped tools require an explicit `organizationId` argument.
13
+
14
+ MCP clients should use their built-in tool listing/search. ChalkSurf does not expose a second search abstraction just to rediscover tools.
15
+
16
+ ## Shared Contract
17
+
18
+ The MCP tools and CLI commands are backed by the same agent resource contracts in `packages/shared/src/agent-tools`.
19
+
20
+ Read tools return structured resource data. Write tools return a structured `receipt`:
21
+
22
+ - `resource`: the resource type and ID affected by the write
23
+ - `operation`: the stable operation name, such as `update_exercise`
24
+ - `changedPaths`: field paths changed by the operation
25
+ - `precondition`: optimistic update status, including `expectedUpdatedAt` and `actualUpdatedAt`
26
+ - `warnings`: non-fatal conditions the agent should surface
27
+ - `details`: operation-specific audit data, such as exercise copy mappings
28
+
29
+ Treat write receipts as an agent audit log. They are suitable for user-visible summaries after an agent edits a sheet.
30
+
31
+ ## Tool Surface
32
+
33
+ | Tool | Permission | Purpose |
34
+ | --- | --- | --- |
35
+ | `get_auth_status` | read | Confirm the authenticated ChalkSurf user and selected organization. |
36
+ | `list_organizations` | read | List organizations granted to the MCP client. |
37
+ | `search_exercises` | read | Search exercises by text, labels, status, ownership, age, difficulty, or semantic query. |
38
+ | `get_exercise` | read | Fetch full details for one visible exercise. |
39
+ | `get_exercise_usage` | read | Inspect which sheets use one visible exercise. |
40
+ | `copy_exercise` | write | Copy one visible exercise into the selected organization. |
41
+ | `update_exercise` | write | Patch exercise fields with an `updated_at` precondition and shared-usage acknowledgement. |
42
+ | `import_exercise` | write | Queue an exercise import from MCP files or HTTPS URLs. |
43
+ | `import_exercise_solution` | write | Queue a solution import for one exercise. |
44
+ | `search_sheets` | read | Search visible sheets, optionally by readiness issues. |
45
+ | `get_sheet` | read | Fetch full details for one visible sheet. |
46
+ | `get_sheet_issues` | read | Fetch actionable sheet issues: invalid LaTeX, missing translations, missing solutions, and missing scores. |
47
+ | `copy_sheet` | write | Copy a visible sheet, either keeping exercise references or copying all visible exercises. |
48
+ | `update_sheet` | write | Patch sheet metadata with an `updated_at` precondition. |
49
+ | `append_exercises_to_sheet` | write | Append existing visible exercises to the end of one writable sheet. |
50
+ | `list_folders` | read | List sheet folders in the selected organization. |
51
+ | `create_folder` | write | Create a sheet folder. |
52
+ | `rename_folder` | write | Rename a sheet folder. |
53
+ | `validate_latex_snippets` | read | Validate proposed LaTeX snippets before writing. |
54
+ | `import_sheet` | write | Queue sheet imports from MCP files or HTTPS URLs. |
55
+ | `import_sheet_solutions` | write | Queue solution import for an existing sheet. |
56
+ | `list_jobs` | read | List jobs in the granted organization. |
57
+ | `get_job` | read | Fetch one job. |
58
+ | `wait_for_jobs` | read | Wait for one or more jobs to complete. |
59
+
60
+ No MCP tool currently deletes resources or transfers resources between organizations.
61
+
62
+ ## Common Workflows
63
+
64
+ ### Fix LaTeX Errors In A Sheet
65
+
66
+ 1. Call `get_sheet` for current resource details and `updated_at` values.
67
+ 2. Call `get_sheet_issues` and filter `issues` to `type: "invalid_latex"`.
68
+ 3. Use each issue's `patchPath` to build the smallest exercise or sheet patch.
69
+ 4. Call `validate_latex_snippets` with the proposed replacement text.
70
+ 5. Call `update_exercise` or `update_sheet` with the latest `expectedUpdatedAt`.
71
+ 6. Call `get_sheet_issues` again and stop when the relevant issue count is zero.
72
+
73
+ For exercise updates, pass `targetSheetId` when the repair belongs to one sheet. Only pass `allowSharedExerciseUpdate: true` after the user acknowledges that the exercise may be used elsewhere.
74
+
75
+ ### Create A Slightly Easier Variant Sheet
76
+
77
+ 1. Call `copy_sheet` with `exerciseCopyMode: "copy_all_exercises"`.
78
+ 2. Read `receipt.resource.id` for the copied sheet ID.
79
+ 3. Read `receipt.details.exerciseIdMap` to map source exercise IDs to editable copied exercise IDs.
80
+ 4. Call `get_sheet` on the copied sheet.
81
+ 5. Apply focused `update_exercise` patches to the copied exercises.
82
+ 6. Call `get_sheet_issues` before reporting completion.
83
+
84
+ Use `exerciseCopyMode: "keep_references"` only when the user wants a new sheet that still points at the original exercises.
85
+
86
+ ## CLI Mapping
87
+
88
+ The closest CLI equivalents are documented in [the agent guide](./agents.md). Use the CLI when a workflow already runs in a shell or CI job; use MCP when the agent host has native MCP support and can complete ChalkSurf OAuth.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chalksurf/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -25,7 +25,7 @@
25
25
  "scripts": {
26
26
  "dev": "node --import tsx ./src/bin/chalksurf.ts",
27
27
  "prepack": "npm run build",
28
- "build": "tsc --project ./tsconfig.build.json",
28
+ "build": "node --import tsx ./scripts/build.ts",
29
29
  "lint": "eslint .",
30
30
  "type-check": "tsc --noEmit --project ./tsconfig.json",
31
31
  "test": "vitest --config ./vitest.config.ts --run --maxWorkers=1",
@@ -40,6 +40,7 @@
40
40
  "devDependencies": {
41
41
  "@types/node": "^24.10.1",
42
42
  "@types/yargs": "^17.0.35",
43
+ "esbuild": "^0.27.0",
43
44
  "tsx": "^4.20.6",
44
45
  "typescript": "^5.0.0",
45
46
  "vitest": "^4.1.2"
@@ -1,174 +0,0 @@
1
- import { createApiClient } from '../lib/api-client.js';
2
- import { CliCommandError } from '../lib/cli-error.js';
3
- import { resolveOrganization } from '../lib/config-store.js';
4
- import { formatUserIdentity, mapApiErrorToCliError, requireResolvedBaseUrl, requireResolvedToken, } from '../lib/session.js';
5
- const isInteractiveStdin = (stdin) => {
6
- return stdin.isTTY === true;
7
- };
8
- const readTokenFromStdin = async (stdin) => {
9
- let token = '';
10
- for await (const chunk of stdin) {
11
- token += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
12
- }
13
- return token.trim();
14
- };
15
- const serializeOrganization = (organization) => {
16
- if (!organization) {
17
- return null;
18
- }
19
- return {
20
- id: organization.organizationId,
21
- name: organization.organizationName,
22
- role: organization.role,
23
- type: organization.organizationType,
24
- };
25
- };
26
- const formatOrganizationSummary = (organization) => {
27
- if (!organization) {
28
- return 'No default organization selected.';
29
- }
30
- return `Default organization: ${organization.name} (${organization.id}).`;
31
- };
32
- export const registerAuthCommands = (authYargs, context) => {
33
- return authYargs
34
- .command('login', 'Read a CLI token from stdin or prompt for it interactively', (loginYargs) => {
35
- return loginYargs
36
- .option('with-token', {
37
- type: 'boolean',
38
- default: false,
39
- describe: 'Read the token from stdin when piping input',
40
- })
41
- .example('chalksurf auth login --profile prod-cztamas --base-url https://chalksurf-api.fly.dev', 'Prompt for a CLI token and store it in the prod-cztamas profile')
42
- .example(`printf '%s' "$CHALKSURF_TOKEN" | chalksurf auth login --profile prod-codex --base-url https://chalksurf-api.fly.dev`, 'Read a CLI token from stdin into an explicit profile');
43
- }, async (argv) => {
44
- const config = await context.configStore.loadProfile({ profileName: argv.profile });
45
- const resolvedBaseUrl = requireResolvedBaseUrl({
46
- flagValue: argv.baseUrl,
47
- env: context.env,
48
- config,
49
- });
50
- const token = (isInteractiveStdin(context.stdin)
51
- ? await context.promptSecret('CLI token: ')
52
- : await readTokenFromStdin(context.stdin)).trim();
53
- if (!token) {
54
- throw new CliCommandError('No token was provided.', 2);
55
- }
56
- const apiClient = createApiClient({
57
- baseUrl: resolvedBaseUrl.value,
58
- token,
59
- });
60
- let profile;
61
- try {
62
- profile = await apiClient.getUserProfile();
63
- }
64
- catch (error) {
65
- throw mapApiErrorToCliError(error);
66
- }
67
- const resolvedOrganization = resolveOrganization({
68
- flagValue: argv.organization,
69
- env: context.env,
70
- config,
71
- profile,
72
- });
73
- await context.configStore.update({ profileName: argv.profile }, (currentConfig) => ({
74
- ...currentConfig,
75
- token,
76
- baseUrl: resolvedBaseUrl.value,
77
- organizationId: resolvedOrganization.organization?.organizationId,
78
- }));
79
- resolvedOrganization.warnings.forEach((warning) => {
80
- context.output.info(warning);
81
- });
82
- context.output.print({
83
- authenticated: true,
84
- baseUrl: resolvedBaseUrl.value,
85
- baseUrlSource: resolvedBaseUrl.source,
86
- organization: serializeOrganization(resolvedOrganization.organization),
87
- profile: config.profileName,
88
- profileSource: config.profileSource,
89
- user: {
90
- email: profile.email,
91
- id: profile.id,
92
- name: profile.name,
93
- },
94
- }, (result) => `Logged in as ${formatUserIdentity(result.user)}.\n${formatOrganizationSummary(result.organization)}`, {
95
- command: 'auth login',
96
- });
97
- })
98
- .command('status', 'Show the current authentication and organization state', (statusYargs) => statusYargs
99
- .example('chalksurf auth status', 'Show the current authenticated user and default organization')
100
- .example('chalksurf --profile prod-codex auth status --json', 'Inspect a specific profile in machine-readable mode')
101
- .epilogue([
102
- 'Resolution order:',
103
- ' profile: --profile, then CHALKSURF_PROFILE, then stored default profile',
104
- ' base URL: --base-url, then CHALKSURF_BASE_URL, then active profile',
105
- ' token: CHALKSURF_TOKEN, then active profile',
106
- ' organization: --organization, then CHALKSURF_ORGANIZATION_ID, then active profile',
107
- ].join('\n')), async (argv) => {
108
- const config = await context.configStore.loadProfile({ profileName: argv.profile });
109
- const resolvedBaseUrl = requireResolvedBaseUrl({
110
- flagValue: argv.baseUrl,
111
- env: context.env,
112
- config,
113
- });
114
- const resolvedToken = requireResolvedToken({
115
- env: context.env,
116
- config,
117
- });
118
- const apiClient = createApiClient({
119
- baseUrl: resolvedBaseUrl.value,
120
- token: resolvedToken.value,
121
- });
122
- let profile;
123
- try {
124
- profile = await apiClient.getUserProfile();
125
- }
126
- catch (error) {
127
- throw mapApiErrorToCliError(error);
128
- }
129
- const resolvedOrganization = resolveOrganization({
130
- flagValue: argv.organization,
131
- env: context.env,
132
- config,
133
- profile,
134
- });
135
- resolvedOrganization.warnings.forEach((warning) => {
136
- context.output.info(warning);
137
- });
138
- context.output.print({
139
- authenticated: true,
140
- baseUrl: resolvedBaseUrl.value,
141
- baseUrlSource: resolvedBaseUrl.source,
142
- organization: serializeOrganization(resolvedOrganization.organization),
143
- organizationSource: resolvedOrganization.source,
144
- profile: config.profileName,
145
- profileSource: config.profileSource,
146
- tokenSource: resolvedToken.source,
147
- user: {
148
- email: profile.email,
149
- id: profile.id,
150
- name: profile.name,
151
- },
152
- }, (result) => `Authenticated as ${formatUserIdentity(result.user)}.\n${formatOrganizationSummary(result.organization)}`, {
153
- command: 'auth status',
154
- });
155
- })
156
- .command('logout', 'Remove the locally stored CLI token', (logoutYargs) => logoutYargs.example('chalksurf auth logout', 'Remove the stored CLI token from local config'), async (argv) => {
157
- const config = await context.configStore.loadProfile({ profileName: argv.profile });
158
- const hadStoredToken = Boolean(config.token);
159
- await context.configStore.update({ profileName: argv.profile }, (currentConfig) => ({
160
- ...currentConfig,
161
- token: undefined,
162
- }));
163
- context.output.print({
164
- clearedStoredToken: hadStoredToken,
165
- loggedOut: true,
166
- profile: config.profileName,
167
- profileSource: config.profileSource,
168
- }, hadStoredToken ? 'Stored CLI token removed.' : 'No stored CLI token was present.', {
169
- command: 'auth logout',
170
- });
171
- })
172
- .demandCommand(1)
173
- .strict();
174
- };