@remits/remits-cli 0.1.98 → 0.1.100
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/README.md +2 -0
- package/index.js +83 -10
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +150 -30
package/README.md
CHANGED
|
@@ -34,6 +34,8 @@ remits-cli components sync --branch feature_branch --dry-run --summary
|
|
|
34
34
|
remits-cli components sync --branch feature_branch --force-tombstones
|
|
35
35
|
remits-cli token --path page/my-embeddable
|
|
36
36
|
remits-cli token --path page/my-embeddable --variant-branch feature_branch
|
|
37
|
+
remits-cli token inspect --token https://example.test/s/<tokenKey>/page/my-embeddable
|
|
38
|
+
remits-cli tool --name mcp_account_user_admin --data-mode prod --input '{"action":"account_create","parentAccountId":4,"name":"Freto","type":"PRODUCT","dryRun":true}'
|
|
37
39
|
remits-cli data-mode
|
|
38
40
|
remits-cli data-mode set prod
|
|
39
41
|
remits-cli sessions list
|
package/index.js
CHANGED
|
@@ -278,9 +278,29 @@ function printResolvedBaseUrl(baseUrl) {
|
|
|
278
278
|
console.log('Base URL:', normalizeBaseUrl(baseUrl || DEFAULT_BASE_URL));
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
// Tools whose
|
|
282
|
-
//
|
|
281
|
+
// Tools/actions whose names signal they mutate state rather than only reading it. Used solely to make
|
|
282
|
+
// data-lane banners precise — never to block or permit anything.
|
|
283
283
|
const MUTATING_TOOL_PATTERN = /(^|_)(patch|edit|create|commit|update|delete|remove|write|set|assign|run|execute|send|pause|unpause|interrupt|restore|repair|migrate|sync)(_|$)/i;
|
|
284
|
+
const MUTATING_TOOL_ACTION_PATTERN = /(^|_)(patch|edit|create|commit|update|delete|remove|write|set|assign|run|execute|send|pause|unpause|interrupt|restore|repair|migrate|sync|add|upsert|reparent|structure|accept|complete|release|record|clear|retire|subscribe|unsubscribe|stop|cancel|kill)(_|$)/i;
|
|
285
|
+
|
|
286
|
+
function toolInputAction(input) {
|
|
287
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
|
|
288
|
+
const value = input.action || input.controlAction || input.command || input.mode;
|
|
289
|
+
if (value === undefined || value === null || value === true) return null;
|
|
290
|
+
const normalized = String(value).trim();
|
|
291
|
+
return normalized || null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function isMutatingToolCall(toolName, input) {
|
|
295
|
+
if (MUTATING_TOOL_PATTERN.test(String(toolName || ''))) return true;
|
|
296
|
+
const action = toolInputAction(input);
|
|
297
|
+
return action ? MUTATING_TOOL_ACTION_PATTERN.test(action) : false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function toolOperationLabel(toolName, input) {
|
|
301
|
+
const action = toolInputAction(input);
|
|
302
|
+
return 'tool ' + String(toolName) + (action ? ' action ' + action : '');
|
|
303
|
+
}
|
|
284
304
|
|
|
285
305
|
function looksLikeDryRun(input) {
|
|
286
306
|
if (!input || typeof input !== 'object') return false;
|
|
@@ -294,25 +314,29 @@ function isLocalBaseUrl(baseUrl) {
|
|
|
294
314
|
}
|
|
295
315
|
|
|
296
316
|
/**
|
|
297
|
-
* One consistent banner across
|
|
298
|
-
*
|
|
299
|
-
*
|
|
317
|
+
* One consistent banner across risky data-lane surfaces. Agents have repeatedly crossed prod/test
|
|
318
|
+
* boundaries by assumption, so the banner is loud, states the account it resolved, and distinguishes
|
|
319
|
+
* live production writes from isolated test-data writes.
|
|
300
320
|
*/
|
|
301
321
|
function printProdDataBanner({ dataMode, accountId, baseUrl, operation, mutating = false, dryRun = false }) {
|
|
302
322
|
const normalizedDataMode = String(dataMode || '').toLowerCase();
|
|
303
323
|
const prodData = normalizedDataMode === 'prod';
|
|
304
324
|
const prodHost = baseUrl && !isLocalBaseUrl(baseUrl);
|
|
305
|
-
if (!prodData && !prodHost) return;
|
|
325
|
+
if (!prodData && !prodHost && !mutating) return;
|
|
306
326
|
const bar = '='.repeat(72);
|
|
307
327
|
let headline;
|
|
308
328
|
if (prodData) {
|
|
309
329
|
headline = dryRun
|
|
310
330
|
? 'PROD DATA — DRY RUN (no write will be attempted)'
|
|
311
331
|
: (mutating ? 'PROD DATA WRITE — this runs against LIVE production data' : 'PROD DATA READ — this reads LIVE production data');
|
|
312
|
-
} else {
|
|
332
|
+
} else if (prodHost) {
|
|
313
333
|
headline = dryRun
|
|
314
334
|
? 'PROD HOST / TEST DATA — DRY RUN'
|
|
315
335
|
: (mutating ? 'PROD HOST / TEST DATA WRITE — deployed app, isolated test data' : 'PROD HOST / TEST DATA READ — deployed app, isolated test data');
|
|
336
|
+
} else {
|
|
337
|
+
headline = dryRun
|
|
338
|
+
? 'TEST DATA — DRY RUN (no write will be attempted)'
|
|
339
|
+
: 'TEST DATA WRITE — isolated test data';
|
|
316
340
|
}
|
|
317
341
|
console.log(bar);
|
|
318
342
|
console.log(' ' + headline);
|
|
@@ -421,6 +445,17 @@ function sessionJsonlFile(cwd) {
|
|
|
421
445
|
}
|
|
422
446
|
|
|
423
447
|
const CONTENT_FIELDS = new Set(['source', 'html', 'javascript', 'css', 'schema', 'inputSchema', 'previewData', 'messages']);
|
|
448
|
+
const SECRET_LOG_FIELDS = new Set([
|
|
449
|
+
'token',
|
|
450
|
+
'tokeninput',
|
|
451
|
+
'tokenkey',
|
|
452
|
+
'authtoken',
|
|
453
|
+
'xauthtoken',
|
|
454
|
+
'x-auth-token',
|
|
455
|
+
'remitstoken',
|
|
456
|
+
'x-remits-token',
|
|
457
|
+
'authorization'
|
|
458
|
+
]);
|
|
424
459
|
|
|
425
460
|
function sanitizeForLog(value) {
|
|
426
461
|
if (value === null || value === undefined) {
|
|
@@ -434,7 +469,7 @@ function sanitizeForLog(value) {
|
|
|
434
469
|
}
|
|
435
470
|
const out = {};
|
|
436
471
|
for (const [k, v] of Object.entries(value)) {
|
|
437
|
-
if (k.toLowerCase()
|
|
472
|
+
if (SECRET_LOG_FIELDS.has(k.toLowerCase())) {
|
|
438
473
|
out[k] = '[redacted]';
|
|
439
474
|
} else if (CONTENT_FIELDS.has(k) && typeof v === 'string') {
|
|
440
475
|
out[k] = '[' + v.length + ' chars]';
|
|
@@ -2777,6 +2812,12 @@ async function testCommand(flags) {
|
|
|
2777
2812
|
}
|
|
2778
2813
|
|
|
2779
2814
|
async function tokenCommand(flags) {
|
|
2815
|
+
const subcommand = flags._ && flags._[1];
|
|
2816
|
+
if (subcommand === 'inspect' || subcommand === 'details' || subcommand === 'decode') {
|
|
2817
|
+
await tokenInspectCommand(flags);
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2780
2821
|
const cwd = process.cwd();
|
|
2781
2822
|
ensureLocalState(cwd);
|
|
2782
2823
|
const sessionContext = resolveSessionContext(cwd, flags);
|
|
@@ -2829,6 +2870,35 @@ async function tokenCommand(flags) {
|
|
|
2829
2870
|
console.log(JSON.stringify(output, null, 2));
|
|
2830
2871
|
}
|
|
2831
2872
|
|
|
2873
|
+
async function tokenInspectCommand(flags) {
|
|
2874
|
+
const cwd = process.cwd();
|
|
2875
|
+
ensureLocalState(cwd);
|
|
2876
|
+
const sessionContext = resolveSessionContext(cwd, flags);
|
|
2877
|
+
const { session, accountId } = sessionContext;
|
|
2878
|
+
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
2879
|
+
const positional = flags._ && flags._[2];
|
|
2880
|
+
const tokenInput = flags.token || flags['token-key'] || flags.tokenKey || flags.value || flags.url || positional;
|
|
2881
|
+
|
|
2882
|
+
if (!tokenInput || tokenInput === true) {
|
|
2883
|
+
throw new Error('Token inspection requires --token <token|tokenKey|URL>, --token-key <key>, --url <URL>, or a positional value.');
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
const api = buildAxios(baseUrl, session.token);
|
|
2887
|
+
const data = await loggedPost(api, cwd, '/cli/tokenInspect', {
|
|
2888
|
+
token: session.token,
|
|
2889
|
+
accountId,
|
|
2890
|
+
tokenInput
|
|
2891
|
+
}).then((r) => r.data);
|
|
2892
|
+
|
|
2893
|
+
if (!data.success) {
|
|
2894
|
+
throw new Error(data.message || 'Failed to inspect token');
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
printSessionResolutionWarning(sessionContext);
|
|
2898
|
+
printResolvedBaseUrl(baseUrl);
|
|
2899
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2832
2902
|
async function toolsCommand(flags) {
|
|
2833
2903
|
const cwd = process.cwd();
|
|
2834
2904
|
ensureLocalState(cwd);
|
|
@@ -2957,8 +3027,8 @@ async function toolCommand(flags) {
|
|
|
2957
3027
|
dataMode,
|
|
2958
3028
|
accountId,
|
|
2959
3029
|
baseUrl,
|
|
2960
|
-
operation:
|
|
2961
|
-
mutating:
|
|
3030
|
+
operation: toolOperationLabel(toolName, input),
|
|
3031
|
+
mutating: isMutatingToolCall(toolName, input),
|
|
2962
3032
|
dryRun: looksLikeDryRun(input)
|
|
2963
3033
|
});
|
|
2964
3034
|
|
|
@@ -5524,8 +5594,10 @@ function printToolsHelp() {
|
|
|
5524
5594
|
|
|
5525
5595
|
function printTokenHelp() {
|
|
5526
5596
|
console.log('Usage: remits-cli token [--base-url URL] [--branch BRANCH] [--path embeddable/path] [--data-mode test|prod] [--as-account ID] [--variant-branch NAME|none]');
|
|
5597
|
+
console.log(' remits-cli token inspect --token <token|tokenKey|URL> [--base-url URL] [--account-id ID]');
|
|
5527
5598
|
console.log('');
|
|
5528
5599
|
console.log('Mints a branch-aware browser URL for embeddable verification.');
|
|
5600
|
+
console.log('Inspect decodes a persisted Remits token and prints token metadata, recognized routing fields, safety/dataMode evidence, and full context.');
|
|
5529
5601
|
}
|
|
5530
5602
|
|
|
5531
5603
|
async function main() {
|
|
@@ -5594,6 +5666,7 @@ async function main() {
|
|
|
5594
5666
|
console.log(' remits-cli components branch <name> --retire [--force] # delete the branch\'s overlays');
|
|
5595
5667
|
console.log(' remits-cli test run --test <id|name> [--base-url URL] [--names name1,name2] [--watch true|false] [--data-mode test|prod] [--as-account ID] [--variant-branch NAME]');
|
|
5596
5668
|
console.log(' remits-cli token [--base-url URL] [--branch BRANCH] [--path embeddable/path] [--data-mode test|prod] [--as-account ID] [--variant-branch NAME|none]');
|
|
5669
|
+
console.log(' remits-cli token inspect --token <token|tokenKey|URL> [--base-url URL] [--account-id ID]');
|
|
5597
5670
|
console.log('');
|
|
5598
5671
|
console.log(' --as-account <ID> verifies AS a descendant subscriber account, so its relationship edge');
|
|
5599
5672
|
console.log(' selects the component branch and the run resolves exactly what production will.');
|
package/package.json
CHANGED
|
@@ -89,6 +89,7 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
|
|
|
89
89
|
- [`mcp_run_agent`](#mcp_run_agent)
|
|
90
90
|
- [Controlling a live agent — `pause` / `unpause` / `interrupt`](#controlling-a-live-agent--pause--unpause--interrupt)
|
|
91
91
|
- [`mcp_system_logs`](#mcp_system_logs)
|
|
92
|
+
- [`mcp_user_activity`](#mcp_user_activity)
|
|
92
93
|
- [`mcp_performance_trace`](#mcp_performance_trace)
|
|
93
94
|
- [`mcp_event_diagnostics`](#mcp_event_diagnostics)
|
|
94
95
|
- [`mcp_component_view`](#mcp_component_view)
|
|
@@ -160,9 +161,35 @@ Two more, easily confused: top-level **`componentBranches`** lists the variant b
|
|
|
160
161
|
account type and parent hierarchy first, and work only from an existing indexed repo unless the user
|
|
161
162
|
explicitly asks you to clone one.
|
|
162
163
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
For access questions — "who can see this client account?", "why does this user see the wrong data?" — use
|
|
165
|
+
`mcp_account_user_admin` first (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use
|
|
166
|
+
`mcp_sql_query` only when you need raw join-table investigation. Remember a user's custom fields are stored
|
|
167
|
+
**per bound account**, so the same person can differ per account.
|
|
168
|
+
|
|
169
|
+
**Test-data flags are first-class safety evidence.** `Account.testAccount` and `User.testUser` are set for
|
|
170
|
+
accounts/users created while the execution data lane is `test` (including `mcp_account_user_admin`
|
|
171
|
+
creation paths); prod-data creates leave them false. Updating an existing real account/user in test mode
|
|
172
|
+
does not convert it into test data, but its Firestore extension-field writes still go to the test lane.
|
|
173
|
+
`Object.testMode` / `Event.testMode` / `Alert.testMode` identify lifecycle rows in the test data lane.
|
|
174
|
+
Agent-facing surfaces expose these fields:
|
|
175
|
+
|
|
176
|
+
- `account-info.json`, `account-hierarchy.json`, and `mcp_account_view`: `resolution.testAccount` for the
|
|
177
|
+
described account, and `testAccount` on returned hierarchy nodes.
|
|
178
|
+
- `mcp_account_user_admin`: `testAccount` on `hierarchy`, `account`, and `account_create` results;
|
|
179
|
+
`testUser` on `users` / `user` results.
|
|
180
|
+
- `mcp_record_listing` / `mcp_record_view`: `testMode` on `object`, `event`, and `alert` records.
|
|
181
|
+
- `mcp_object_activity`: top-level `dataMode`, `object.testMode`, and `testMode` on Event/Alert
|
|
182
|
+
timeline entries.
|
|
183
|
+
- `mcp_event_diagnostics`: top-level `dataMode` and `result.event.testMode`.
|
|
184
|
+
- `remits-cli token inspect`: owner `account.testAccount` and `user.testUser`, plus token `dataMode`.
|
|
185
|
+
|
|
186
|
+
Persisted AI session rows currently do **not** store durable per-grouping `testMode` / `dataMode`; use
|
|
187
|
+
`mcp_ai_session_search.dataMode` only as the current tool execution lane, not as proof of the historical
|
|
188
|
+
grouping's data lane.
|
|
189
|
+
|
|
190
|
+
If any of those fields contradict the lane you intended, stop and rerun the command with an explicit
|
|
191
|
+
`--data-mode test` or `--data-mode prod`. Never infer prod/test from an account name, URL, branch name, or
|
|
192
|
+
the mere existence of a created account.
|
|
166
193
|
|
|
167
194
|
Use `remits-cli` for everything else: staging changes, running tests, generating embeddable tokens,
|
|
168
195
|
committing work, and diagnosing production issues.
|
|
@@ -398,6 +425,7 @@ Use `mcp_support_ticket` to manage lifecycle:
|
|
|
398
425
|
- `complete` — resolve the ticket with a summary of what was done
|
|
399
426
|
- `release` — unassign if you cannot continue
|
|
400
427
|
- **Pulling a ticket by number is enough — you do NOT need to know its account first.** The `ticketId` IS the ticket's globally-unique anchor id, and `mcp_support_ticket` resolves the owning account from it for every action that operates on an existing ticket (`read`, `accept`, `update_status`, `complete`, `release`, `record_progress`, `add_artifact`, `get_attachment`). So when a user says "pull ticket 19463", just call `read` with `{ "action": "read", "ticketId": "19463" }` from any authenticated **prod** session (the ticket lives in prod data) — omit `accountId` entirely. The response returns the resolved `accountId`/`accountName` (and `implementationAccountId` when set); use those for any follow-up work. **Only `create` requires an explicit `accountId`** (a brand-new ticket has no anchor to resolve from). If a bare `ticketId` returns "Support ticket not found", double-check you are in `--data-mode prod`, then fall back to passing an explicit `accountId`.
|
|
428
|
+
- `read` can return `documentState:'missing_or_empty'` with `ticket.mirrorOnly:true` and `ticket.canMutate:false`. That means the support-ticket anchor exists and the queue row is real, but the backing Firestore `support_tickets/{ticketId}` document is missing or metadata-only. Treat this as a degraded ticket, not as "ticket not found"; run or request the System Account action `Restore Support Ticket Documents From Anchor Mirrors` for the owning account before lifecycle mutations. The restore recovers mirrored scalar fields, but document-only arrays such as alert snapshots, email threads, attachments, worklog entries, and artifacts may be lost.
|
|
401
429
|
- If a ticket is part of the request, manage the lifecycle proactively. Do not wait for the human user to remind you to read, accept, update, complete, or release it.
|
|
402
430
|
|
|
403
431
|
Ticket-routing context:
|
|
@@ -512,6 +540,7 @@ here is the tool and the filterable fields:
|
|
|
512
540
|
| `object_log` | `mcp_record_listing` / `mcp_record_view` | `type`, `description`, `content`, `threadGroupingId` |
|
|
513
541
|
| `event` | `mcp_record_listing` / `mcp_record_view` | `action`, `status`, `eventDate`, `threadGroupingId` |
|
|
514
542
|
| `alert` | `mcp_record_listing` / `mcp_record_view` | `status`, `type`, `active`, `threadGroupingId` |
|
|
543
|
+
| user activity session | `mcp_user_activity` | `userId`, `accountId`, `sessionKey`, `focusedOnly`, `traceId` pivots |
|
|
515
544
|
|
|
516
545
|
`mcp_record_listing` finds candidates when you do not know the id; `mcp_record_view` opens an exact one;
|
|
517
546
|
`mcp_object_activity` returns one Object's whole timeline in order.
|
|
@@ -525,6 +554,8 @@ different lanes (see `platform-overview.md` → *Test and Production Data Lanes*
|
|
|
525
554
|
- **`threadGroupingId`** — groups every record and log line from one processing chain, and is also the
|
|
526
555
|
request's trace id. Pivot into `mcp_system_logs` and `mcp_performance_trace`.
|
|
527
556
|
- **`node`** — resolves to `serviceName` + `region` for log queries (table below).
|
|
557
|
+
- **`sessionKey`** — salted user-activity session key. Pivot `mcp_user_activity` `sessions` → `story`;
|
|
558
|
+
each beat then carries a `traceId` / `threadGroupingId` for trace and log investigation.
|
|
528
559
|
- **`sessionId`** — pivots into persisted AI activity via `mcp_ai_session_search`.
|
|
529
560
|
|
|
530
561
|
### Reading a record's `content` — persisted context, not a memory dump
|
|
@@ -790,7 +821,7 @@ the file. Omit the key; the platform fills it in on sync:
|
|
|
790
821
|
```yaml
|
|
791
822
|
# components/embeddables/new_MerchantPortal.meta.yml — no `id:` yet
|
|
792
823
|
name: Merchant Portal
|
|
793
|
-
summary: One-line statement of what this component is for.
|
|
824
|
+
summary: One-line statement of what this component is for. This is the compact text account-info.json uses first.
|
|
794
825
|
description: |
|
|
795
826
|
Longer technical description with line-number references to the key logic.
|
|
796
827
|
path: /page/merchant-portal # Readers and Embeddables only
|
|
@@ -901,13 +932,15 @@ If the work is tied to a support ticket:
|
|
|
901
932
|
|
|
902
933
|
Before committing, update metadata so the next session understands what changed:
|
|
903
934
|
|
|
904
|
-
1. **`.meta.yml` sidecars** — Update `description
|
|
935
|
+
1. **`.meta.yml` sidecars** — Update `summary`, `description`, and `mermaid` for each modified component. `summary` is what drives the compact component description in generated `account-info.json`; `description` is the fallback when no summary is set and is capped in that file.
|
|
905
936
|
2. **`README.md`** — If the change affects account-level capabilities or workflows.
|
|
906
937
|
3. **New components** — Always fill in `.meta.yml` immediately.
|
|
907
938
|
|
|
908
|
-
`account-info.json` is read-only — never edit it. It regenerates automatically after sync.
|
|
909
|
-
|
|
910
|
-
|
|
939
|
+
`account-info.json` is read-only — never edit it. It regenerates automatically after sync. Component
|
|
940
|
+
entries prefer `summary`, fall back to capped `description`, and cap `mermaid`; relationships remain as
|
|
941
|
+
generated. On a trunk sync it describes the owning repo account. On a subscriber-initiated variant sync it
|
|
942
|
+
describes the subscribing account reached through the branch edge, even though the component files still
|
|
943
|
+
belong to the owner's repo.
|
|
911
944
|
|
|
912
945
|
#### Temporary Experiment Workflow
|
|
913
946
|
|
|
@@ -1182,8 +1215,9 @@ link carries what?"
|
|
|
1182
1215
|
the edge `domainName`. An **edge** host wins over the account's own, and additionally supplies the path
|
|
1183
1216
|
travelled — which is what makes that edge's branch variants apply.
|
|
1184
1217
|
|
|
1185
|
-
**Users are not part of this graph** (see `platform-overview.md`).
|
|
1186
|
-
`
|
|
1218
|
+
**Users are not part of this graph** (see `platform-overview.md`). Use `mcp_account_user_admin`
|
|
1219
|
+
(`action:'users'`, `action:'user'`, or `action:'user_update'`) for user membership and account-scoped user
|
|
1220
|
+
fields. Drop to `mcp_sql_query` against `user` / `user_account` only for raw join-table evidence.
|
|
1187
1221
|
|
|
1188
1222
|
## Branched Component Variants (per-account component overrides)
|
|
1189
1223
|
|
|
@@ -1441,9 +1475,19 @@ Before starting an investigation outside the confirmed current repo:
|
|
|
1441
1475
|
5. `mcp_record_listing` — search or filter alerts, events, object logs, or objects when you need to find the suspicious record first.
|
|
1442
1476
|
6. `mcp_record_view` — drill into suspicious entries for full content.
|
|
1443
1477
|
7. `mcp_ai_session_search` — if the workflow involves AI, inspect session groupings, prompts, tool definitions, and responses in human-readable form.
|
|
1444
|
-
8. `
|
|
1445
|
-
9. `
|
|
1446
|
-
10. `
|
|
1478
|
+
8. `mcp_user_activity` — for "user X is slow right now" reports, list sessions by `userId`/`accountId`, open the session story, and use the returned beat pivots.
|
|
1479
|
+
9. `mcp_performance_trace` — for slow/sluggish reports, open the beat `traceId` with `action:"trace"`; use `action:"slowest"` when you only have a broad time window.
|
|
1480
|
+
10. `mcp_system_logs` — correlate via `threadGroupingId` for raw log context when the trace needs supporting log lines.
|
|
1481
|
+
11. `mcp_component_view`/`mcp_component_grep` — explain how the responsible component works.
|
|
1482
|
+
|
|
1483
|
+
**Slow / sluggish user report:**
|
|
1484
|
+
|
|
1485
|
+
1. Resolve the reporting user/account with `mcp_account_user_admin` if you only have an email/name.
|
|
1486
|
+
2. Call `mcp_user_activity` with `action:"sessions"` and `userId` or `accountId`.
|
|
1487
|
+
3. Open the likely row with `action:"story"` and inspect beat labels, status, `ms`, `node`, and `traceId`.
|
|
1488
|
+
4. Open slow or failed beat pivots with `mcp_performance_trace` before querying raw logs.
|
|
1489
|
+
5. Use the returned `mcp_system_logs` pivot only when the trace needs surrounding log lines.
|
|
1490
|
+
6. If there is no live session, call `mcp_user_activity` `action:"watch"` for the user/account, ask for reproduction, then read `sessions`/`story` again. Focused sessions retain sanitized request detail and emit archived `REMITS_ACTIVITY` log lines.
|
|
1447
1491
|
|
|
1448
1492
|
**Error or Alert Investigation:**
|
|
1449
1493
|
1. `mcp_record_listing` — search by alert type, content, error text, action, status, `threadGroupingId`, or other exact-match record properties when you do not yet know the record ID.
|
|
@@ -1557,8 +1601,8 @@ its behavior against component source:
|
|
|
1557
1601
|
`databaseName` (the account's own override, often null) vs `resolvedDatabaseName` (the storage namespace
|
|
1558
1602
|
actually in effect); `branchName` (the repo sync branch) and `lastRepoSync`; `domainName` vs
|
|
1559
1603
|
`resolvedDomainName` (the custom host in effect, which differs when reached through an edge host);
|
|
1560
|
-
`
|
|
1561
|
-
/ `componentOwnerAccountName`.
|
|
1604
|
+
`authPath` / `targetPath` (login and post-login landing routes); `editMode`; and — when a component
|
|
1605
|
+
branch is in effect — `componentBranch` plus `componentOwnerAccountId` / `componentOwnerAccountName`.
|
|
1562
1606
|
- `resolution.relationships` — **every structural link upward**, primary first: `parentAccountId` /
|
|
1563
1607
|
`parentAccountName` / `parentAccountCode` / `parentAccountType`, `primary` (true for the one link that
|
|
1564
1608
|
mirrors the account's primary parent), `active`, and the three independent link-scoped properties
|
|
@@ -1584,15 +1628,16 @@ account).
|
|
|
1584
1628
|
|
|
1585
1629
|
- `action: 'hierarchy'` (default) — the descendant tree trimmed to `depth` (1-10, default 2; nodes cut off
|
|
1586
1630
|
are marked `truncated` and still report their child count) and/or the anchored ancestor chain plus every
|
|
1587
|
-
edge (`direction: 'down'|'up'|'both'`). **This is how you get the deep tree
|
|
1588
|
-
-
|
|
1589
|
-
|
|
1631
|
+
edge (`direction: 'down'|'up'|'both'`). Nodes include `testAccount`. **This is how you get the deep tree
|
|
1632
|
+
account-info.json omits.**
|
|
1633
|
+
- `action: 'account'` — one account's `resolution` block, including `testAccount`, plus masked
|
|
1634
|
+
configuration fields, without paying for the component inventory.
|
|
1590
1635
|
- `action: 'users'` — an account's users at a hierarchy `scope` (`self`/`children`/`parents`/`hierarchy`),
|
|
1591
|
-
optional `email` substring filter. Extension fields are omitted here on purpose:
|
|
1592
|
-
account** and these users are bound to their own.
|
|
1636
|
+
optional `email` substring filter. Rows include `testUser`. Extension fields are omitted here on purpose:
|
|
1637
|
+
they are stored **per account** and these users are bound to their own.
|
|
1593
1638
|
- `action: 'user'` — one user (`userId` or `email`) with roles, account memberships, and extension fields
|
|
1594
|
-
**correctly scoped to the requested account
|
|
1595
|
-
tells you when the fields shown belong to a different account.
|
|
1639
|
+
**correctly scoped to the requested account**, plus `testUser`. It never grants membership as a side
|
|
1640
|
+
effect of a read, and tells you when the fields shown belong to a different account.
|
|
1596
1641
|
- `action: 'account_update'` — write Account-schema configuration `fields`, and/or `name`/`status`
|
|
1597
1642
|
(`ACTIVE`/`ON_HOLD`/`PENDING`).
|
|
1598
1643
|
- `action: 'user_update'` — write User-schema `fields` under the named account, plus `name`/`enabled` and
|
|
@@ -1603,12 +1648,37 @@ account).
|
|
|
1603
1648
|
**Building an account hierarchy** (the structural writes — this is how a coding agent provisions accounts
|
|
1604
1649
|
without a browser):
|
|
1605
1650
|
|
|
1651
|
+
**Data-lane rule for provisioning:** `remits-cli tool` defaults to `--data-mode test`. That is correct for
|
|
1652
|
+
fixtures and rehearsals, but it means `mcp_account_user_admin` `action:'account_create'` creates test-lane
|
|
1653
|
+
accounts unless the command explicitly passes `--data-mode prod`. For real platform/product/customer
|
|
1654
|
+
provisioning, always dry-run in prod first, check the response's `dataMode`, then run the write in prod:
|
|
1655
|
+
|
|
1656
|
+
```bash
|
|
1657
|
+
remits-cli tool --name mcp_account_user_admin --data-mode prod --input '{"action":"account_create","parentAccountId":4,"name":"Freto","type":"PRODUCT","dryRun":true}'
|
|
1658
|
+
```
|
|
1659
|
+
|
|
1660
|
+
After the real write, verify the response (or re-read `action:'account'`) shows the command `dataMode` you
|
|
1661
|
+
intended and `testAccount:false` for real provisioning. `testAccount:true` means you created a test-data
|
|
1662
|
+
account, even if the name and structure look correct.
|
|
1663
|
+
|
|
1664
|
+
For a test rehearsal, make the opposite assertion explicit: the response should show `dataMode:'test'` and
|
|
1665
|
+
`testAccount:true` for created accounts (or `testUser:true` for created users). A false test flag in a
|
|
1666
|
+
test rehearsal means the data lane or the returned entity is not the one you intended.
|
|
1667
|
+
|
|
1668
|
+
Account/User schema `fields` are Firestore-backed extension fields. Their physical storage follows the
|
|
1669
|
+
same data lane as the tool call: `--data-mode test` writes under `testing/<resolvedDatabaseName>/...`, while
|
|
1670
|
+
`--data-mode prod` writes under `accounts/<resolvedDatabaseName>/...` (for modern segmented accounts). If a
|
|
1671
|
+
test-lane rehearsal should become real provisioning, rerun the create/update in prod mode; do not assume the
|
|
1672
|
+
test-lane Firestore fields moved.
|
|
1673
|
+
|
|
1606
1674
|
- `action: 'account_create'` — create a child under `parentAccountId`, with its **primary relationship edge**,
|
|
1607
|
-
applying `type` / `databaseName` / `domainName` / `
|
|
1608
|
-
`editMode` / … **at birth**. That ordering matters: the storage
|
|
1609
|
-
and the parent's cascaded schema fields are written into it
|
|
1610
|
-
same-name account under that parent comes back with
|
|
1611
|
-
|
|
1675
|
+
applying `type` / `databaseName` / `domainName` / `authPath` / `targetPath` / `code` /
|
|
1676
|
+
`repositoryNameOverride` / `branchName` / `editMode` / … **at birth**. That ordering matters: the storage
|
|
1677
|
+
namespace is resolved from those properties, and the parent's cascaded schema fields are written into it
|
|
1678
|
+
during creation. Idempotent — an existing same-name account under that parent comes back with
|
|
1679
|
+
`reusedExisting: true`, unchanged. The response includes `testAccount`.
|
|
1680
|
+
- `action: 'account_structure'` — change those properties on an existing account, including account-level
|
|
1681
|
+
custom host, login path, and landing path.
|
|
1612
1682
|
- `action: 'edge_add'` / `'edge_update'` / `'edge_remove'` — manage a membership `AccountRelationship` edge to
|
|
1613
1683
|
`parentAccountId`, including the three independent edge properties `branchName` (which component code runs),
|
|
1614
1684
|
`databaseName` (a path-scoped storage-namespace override — **live**, and inherited by everything below
|
|
@@ -1732,8 +1802,11 @@ Object lifecycle timeline — metadata + recent activity.
|
|
|
1732
1802
|
|-----------|----------|-------------|
|
|
1733
1803
|
| `accountId` | yes | Account ID |
|
|
1734
1804
|
| `objectId` | yes | Object ID (from `object_id` in documents) |
|
|
1805
|
+
| `dataMode` | no | Explicit lane: `prod` or `test`. Response echoes `dataMode`. |
|
|
1735
1806
|
| `activityOptions` | no | `{limit, offset, types, start, end, order}`. Default: limit=5, order=desc. Types: `OBJECT_LOG`, `EVENT`, `ALERT`. |
|
|
1736
1807
|
|
|
1808
|
+
Response fields include `dataMode`, `object.testMode`, and `testMode` on Event/Alert timeline entries.
|
|
1809
|
+
|
|
1737
1810
|
### `mcp_record_listing`
|
|
1738
1811
|
List and search lifecycle records when you do not already know the record ID.
|
|
1739
1812
|
|
|
@@ -1787,6 +1860,7 @@ Front-stage references:
|
|
|
1787
1860
|
| Parameter | Required | Description |
|
|
1788
1861
|
|-----------|----------|-------------|
|
|
1789
1862
|
| `action` | no | `search` (default), `detail`, or session control `pause`/`unpause`/`interrupt` |
|
|
1863
|
+
| `dataMode` | no | Explicit execution lane: `prod` or `test`. Response echoes `dataMode`, but persisted groupings do not have durable per-row lane flags. |
|
|
1790
1864
|
| `search` | no | Broad text match against session IDs and grouping IDs |
|
|
1791
1865
|
| `sessionId` | no | Session ID filter in search mode, or grouping/session key in detail mode |
|
|
1792
1866
|
| `groupingId` | no | Grouping ID filter in search mode, or grouping key in detail mode |
|
|
@@ -1974,6 +2048,42 @@ Query Cloud Run service logs.
|
|
|
1974
2048
|
{"node": "remitsAdmin-east5", "timeRange": "4h", "severity": "ERROR"}
|
|
1975
2049
|
```
|
|
1976
2050
|
|
|
2051
|
+
### `mcp_user_activity`
|
|
2052
|
+
Read live user activity sessions and control focused capture. Use this before trace/log spelunking when
|
|
2053
|
+
the report is user-centric, for example "User abc is reporting slow responses." It wraps the same
|
|
2054
|
+
Redis-backed store as System → Activity and returns ready pivots to traces, logs, and component source.
|
|
2055
|
+
|
|
2056
|
+
Common flows:
|
|
2057
|
+
|
|
2058
|
+
```bash
|
|
2059
|
+
# Find what one user is doing right now
|
|
2060
|
+
remits-cli tool --name mcp_user_activity --input '{"action":"sessions","userId":3,"limit":10}' --data-mode prod
|
|
2061
|
+
|
|
2062
|
+
# Open a returned sessionKey and inspect its beats
|
|
2063
|
+
remits-cli tool --name mcp_user_activity --input '{"action":"story","sessionKey":"c_46ee68bba67003a6","limit":50}' --data-mode prod
|
|
2064
|
+
|
|
2065
|
+
# Arm focused capture, ask the user to reproduce, then read the story again
|
|
2066
|
+
remits-cli tool --name mcp_user_activity --input '{"action":"watch","userId":3,"minutes":30}' --data-mode prod
|
|
2067
|
+
```
|
|
2068
|
+
|
|
2069
|
+
| Parameter | Required | Description |
|
|
2070
|
+
|-----------|----------|-------------|
|
|
2071
|
+
| `action` | no | `sessions`, `story`, `watch`, `unwatch`, `forget`, `status`, or `archive`. Default: `sessions`. |
|
|
2072
|
+
| `userId` / `accountId` | no | Filter sessions/archive or choose the focus subject for `watch`/`unwatch`. One is required for `watch`/`unwatch`. |
|
|
2073
|
+
| `sessionKey` | for `story`/`forget` | Salted activity session key returned by `sessions`; not a raw browser/session credential. |
|
|
2074
|
+
| `focusedOnly` | no | For `sessions`, return only focused/watched sessions. |
|
|
2075
|
+
| `sinceMs` | no | For `sessions`, lower bound on last-seen epoch milliseconds. Defaults to the activity TTL window. |
|
|
2076
|
+
| `limit` | no | Session/story/archive row cap. Defaults: sessions=100, story=200, archive=50. |
|
|
2077
|
+
| `minutes` | no | Watch TTL for `watch`; defaults to `activity.focus.ttl.minutes`. |
|
|
2078
|
+
| `traceId` / `threadGroupingId` | no | For `archive`, narrow focused activity log pivot to one trace. |
|
|
2079
|
+
| `lookbackHours` | no | For `archive`, Cloud Logging window in the returned `mcp_system_logs` pivot. Default 24, max 168. |
|
|
2080
|
+
| `node` / `serviceName` / `region` | no | For `archive`, target for the returned `mcp_system_logs` pivot. `node` defaults to `remitsAdmin-east5`. |
|
|
2081
|
+
|
|
2082
|
+
Reading rule: use `sessions` → `story` to build the behavioral timeline, then open a slow or failed beat's
|
|
2083
|
+
`mcp_performance_trace` pivot. Use `watch` when the user can reproduce and no live story exists. Use
|
|
2084
|
+
`archive` only for watched/focused sessions; ordinary activity lives in Redis and expires with the activity
|
|
2085
|
+
TTL.
|
|
2086
|
+
|
|
1977
2087
|
### `mcp_performance_trace`
|
|
1978
2088
|
Read Remits request traces through the same `traces(...)` DSL that powers the admin Diagnostics "Request
|
|
1979
2089
|
traces" panel. Use this before raw log spelunking for slow or sluggish requests because it returns profiled
|
|
@@ -2135,6 +2245,7 @@ This keeps file retrieval self-contained — no separate download endpoint is ne
|
|
|
2135
2245
|
|
|
2136
2246
|
**Recommended flow** — `accountId` is optional throughout; `ticketId` resolves the owning account:
|
|
2137
2247
|
1. `read` — check ticket state and any attachments
|
|
2248
|
+
- If `read` returns `ticket.mirrorOnly:true`, use the mirrored fields for triage context, restore the backing document first, then claim/update/complete it.
|
|
2138
2249
|
2. **`accept`** (with `assignee`) — this is a **hard precondition for `update_status`**, not just etiquette
|
|
2139
2250
|
3. `get_attachment` if attachments are present and relevant to the investigation
|
|
2140
2251
|
4. `update_status` — `in_progress` while working, `pending_review` when the fix is done but not yet deployed
|
|
@@ -2300,6 +2411,10 @@ optional class-histogram sampling, **no heap dump and no JFR**.
|
|
|
2300
2411
|
List and filter tickets **across an account and its descendants** so you can choose what to work on. Lifecycle
|
|
2301
2412
|
actions stay on `mcp_support_ticket`.
|
|
2302
2413
|
|
|
2414
|
+
Queue rows are built from support-ticket anchor mirrors by default. A row in this list means a
|
|
2415
|
+
support-ticket anchor exists; it does not guarantee the full Firestore document is healthy. Open the
|
|
2416
|
+
ticket with `mcp_support_ticket read` before lifecycle work and honor `mirrorOnly` / `documentState` if present.
|
|
2417
|
+
|
|
2303
2418
|
| Parameter | Required | Description |
|
|
2304
2419
|
|-----------|----------|-------------|
|
|
2305
2420
|
| `action` | no | `list` (default) |
|
|
@@ -2488,6 +2603,7 @@ remits-cli components branch <name> --unsubscribe <accountId>
|
|
|
2488
2603
|
remits-cli components branch <name> --retire [--force] # delete the branch's overlays
|
|
2489
2604
|
remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
|
|
2490
2605
|
remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
|
|
2606
|
+
remits-cli token inspect --token <token|tokenKey|URL> # inspect token metadata, safety/dataMode evidence, and full context
|
|
2491
2607
|
remits-cli tools [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
|
|
2492
2608
|
remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
|
|
2493
2609
|
remits-cli tool status --call-id <callId> [--data-mode test|prod]
|
|
@@ -2528,7 +2644,10 @@ For tests specifically:
|
|
|
2528
2644
|
|
|
2529
2645
|
- Every command that can touch production (`tool`, `test run`, `components sync`) prints a `PROD DATA`
|
|
2530
2646
|
banner naming the operation, the resolved account, and the host — and distinguishes a live **WRITE**
|
|
2531
|
-
from a live **READ** and from a **DRY RUN**.
|
|
2647
|
+
from a live **READ** and from a **DRY RUN**. `remits-cli tool` also prints an explicit **TEST DATA WRITE**
|
|
2648
|
+
banner for mutating tool calls in the test lane, including multi-action tools such as
|
|
2649
|
+
`mcp_account_user_admin` where the write is signaled by `input.action` (`account_create`, `user_update`,
|
|
2650
|
+
`edge_update`, etc.). If you see a WRITE banner you did not intend, stop.
|
|
2532
2651
|
- A tool call that fails **in the platform runtime** rather than in the tool (Groovy reflective dispatch
|
|
2533
2652
|
of a runtime-compiled component, an empty connection pool, a Redis reconnect, a lock-wait timeout)
|
|
2534
2653
|
now comes back as HTTP `503` with `failureClass: "transient_infrastructure"` and `retryable: true`,
|
|
@@ -2554,13 +2673,14 @@ For tests specifically:
|
|
|
2554
2673
|
| Long-running tool times out | For `mcp_run_action`/`mcp_run_agent`, use the tool's own `executionMode:"async"` and poll with a `controlAction:"status"` call (see "Tool Execution Lifecycle"). For other long tools without their own async, use `remits-cli tool --async true` and poll `remits-cli tool status --call-id <callId>`. `--timeout-ms` only adjusts the per-request HTTP timeout; it is not a substitute for async. |
|
|
2555
2674
|
| Need to continue tracking a long Action or Agent after terminal disconnect | Read `./.remits-cli/tool-responses/<callId>.json` for the returned `actionRunId`/`agentRunId`/`sessionId`/`threadGroupingId`. Re-poll the run with a `controlAction:"status"` call carrying that `actionRunId`/`agentRunId`, or inspect the agent session via `mcp_ai_session_search` with the `sessionId`. |
|
|
2556
2675
|
| Staged change has no effect in a live (non-CLI) run | Staged overrides resolve only under a CLI TestMode (`branchName`+`cliUserId`). Live webhooks and other non-CLI runtime paths still use the DB (trunk, or the account's subscribed variant). `commit` to make it durable. See "Component Resolution". |
|
|
2557
|
-
| Need to know an account's shape (role, type, parents, namespace, branch) | Read `resolution` — from the repo's `account-info.json`, or `mcp_account_user_admin` `action:'account'` (cheap), or `mcp_account_view` (full inventory): `role`/`summary`, `type`, `resolvedDatabaseName`, `relationships`, `componentBranch`, plus top-level `componentBranches`. Never infer structure from the account's name. |
|
|
2676
|
+
| Need to know an account's shape (role, type, parents, namespace, branch, host/login routes) | Read `resolution` — from the repo's `account-info.json`, or `mcp_account_user_admin` `action:'account'` (cheap), or `mcp_account_view` (full inventory): `role`/`summary`, `type`, `resolvedDatabaseName`, `domainName`/`resolvedDomainName`, `authPath`/`targetPath`, `relationships`, `componentBranch`, plus top-level `componentBranches`. Never infer structure from the account's name. |
|
|
2558
2677
|
| Need the account tree below an account, or its users | In a local repo, read `account-hierarchy.json` for the generated tree. For live data, use `mcp_account_user_admin` (`action:'hierarchy'` with a `depth`, or `action:'users'`). `account-info.json` deliberately omits the tree. |
|
|
2678
|
+
| Need to prove whether an account/user/record is test data | Check explicit flags: `resolution.testAccount` / hierarchy `testAccount`, `mcp_account_user_admin` `testAccount` / `testUser`, token inspect owner flags, and `mcp_record_listing` / `mcp_record_view` `testMode` for object/event/alert rows. Do not infer from names or branch labels. |
|
|
2559
2679
|
| Need account configuration values | In a local repo, read `account-configurations.json`. For live data, use `mcp_account_user_admin` (`action:'account'`) or `mcp_account_view`. `account-info.json` deliberately omits configurations. |
|
|
2560
2680
|
| An account has two parents and you don't know which one a run used | `resolution.relationships` lists every link with its own `branchName`/`databaseName`/`domainName`. A membership-only account with SEVERAL edges resolves **trunk and inherits nothing** until a path is named (`--as-account`, `--variant-branch`, or an edge host) — that is by design, not a bug. With exactly ONE membership edge it inherits normally, descendants included. |
|
|
2561
2681
|
| Documents missing / written to the wrong place | Compare `resolution.databaseName` (the account's own override) with `resolution.resolvedDatabaseName` (what is actually in effect), and check for a `databaseName` on one of the `relationships` edges. Data does not inherit; components do. |
|
|
2562
2682
|
| A custom hostname resolves to an unexpected account | Compare `resolution.domainName` with `resolvedDomainName` and the edge `domainName`s. An **edge** host wins over the account's own host and additionally supplies the path travelled (which is what makes that edge's branch variants apply). |
|
|
2563
|
-
| Need users of an account,
|
|
2683
|
+
| Need users of an account, accounts of a user, or account-scoped user fields | Use `mcp_account_user_admin` (`action:'users'`, `action:'user'`, or `action:'user_update'`). Use `mcp_sql_query` on `user` / `user_account` only for raw join-table investigation. Remember user custom fields are stored **per bound account**, so the same person can differ per account. |
|
|
2564
2684
|
| One account behaves differently from its siblings on the same component | It probably subscribes to a **branch variant**. Check `remits-cli components branches` and `remits-cli components branch <name> --subscribers`, and reproduce with `remits-cli test run --as-account <ID>`. Do NOT "fix" this by adding per-account logic to the origin component. |
|
|
2565
2685
|
| Edits on a feature branch seem to run against trunk code | You are likely on the **trunk** branch, or passed `--variant-branch none`. Run `remits-cli components status` — it states which world the working tree resolves. |
|
|
2566
2686
|
| A component vanished for one account after a variant-branch sync | Its file is missing from that branch, so the sync created a **tombstone** that hides it from subscribers. Restore the file on the branch and re-sync. Trunk is unaffected. |
|