@ishlabs/cli 0.17.7 → 0.19.0
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 +54 -54
- package/dist/commands/ask.d.ts +4 -4
- package/dist/commands/ask.js +66 -66
- package/dist/commands/chat.js +10 -10
- package/dist/commands/config.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/iteration.js +57 -57
- package/dist/commands/mcp.d.ts +23 -0
- package/dist/commands/mcp.js +676 -0
- package/dist/commands/person.d.ts +5 -0
- package/dist/commands/{profile.js → person.js} +197 -162
- package/dist/commands/source.d.ts +6 -2
- package/dist/commands/source.js +35 -30
- package/dist/commands/study-analyze.d.ts +1 -1
- package/dist/commands/study-analyze.js +3 -3
- package/dist/commands/study-participant.d.ts +8 -0
- package/dist/commands/{study-tester.js → study-participant.js} +50 -50
- package/dist/commands/study-run.d.ts +6 -6
- package/dist/commands/study-run.js +341 -290
- package/dist/commands/study.js +106 -72
- package/dist/commands/workspace.js +13 -13
- package/dist/connect.js +5 -5
- package/dist/index.js +6 -4
- package/dist/lib/accessibility-profile.d.ts +1 -1
- package/dist/lib/accessibility-profile.js +1 -1
- package/dist/lib/alias-hydrate.js +4 -4
- package/dist/lib/alias-store.d.ts +5 -5
- package/dist/lib/alias-store.js +8 -8
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/billing.d.ts +11 -11
- package/dist/lib/billing.js +16 -16
- package/dist/lib/chat-endpoint-templates.js +1 -1
- package/dist/lib/command-helpers.d.ts +18 -18
- package/dist/lib/command-helpers.js +49 -37
- package/dist/lib/docs.js +570 -387
- package/dist/lib/enums.d.ts +2 -2
- package/dist/lib/enums.js +2 -2
- package/dist/lib/local-sim/browser.d.ts +1 -1
- package/dist/lib/local-sim/browser.js +1 -1
- package/dist/lib/local-sim/debug-report.d.ts +2 -2
- package/dist/lib/local-sim/debug-report.js +3 -3
- package/dist/lib/local-sim/loop.d.ts +5 -5
- package/dist/lib/local-sim/loop.js +38 -38
- package/dist/lib/local-sim/types.d.ts +12 -12
- package/dist/lib/mcp-clients.d.ts +51 -0
- package/dist/lib/mcp-clients.js +175 -0
- package/dist/lib/modality.d.ts +10 -10
- package/dist/lib/modality.js +46 -46
- package/dist/lib/output.d.ts +16 -15
- package/dist/lib/output.js +291 -226
- package/dist/lib/profile-sources.d.ts +64 -16
- package/dist/lib/profile-sources.js +91 -30
- package/dist/lib/skill-content.js +216 -168
- package/dist/lib/study-events.d.ts +3 -3
- package/dist/lib/study-events.js +1 -1
- package/dist/lib/study-inputs.d.ts +11 -1
- package/dist/lib/study-inputs.js +68 -17
- package/dist/lib/study-participants.d.ts +32 -0
- package/dist/lib/study-participants.js +12 -0
- package/dist/lib/types.d.ts +104 -34
- package/package.json +1 -1
- package/dist/commands/profile.d.ts +0 -5
- package/dist/commands/study-tester.d.ts +0 -8
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ish source — Upload and inspect
|
|
2
|
+
* ish source — Upload and inspect participant attachments used as generation inputs.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Attachments (transcripts, audio, images, PDFs) are inputs to `ish person
|
|
5
5
|
* generate`. For one-shot generation, `profile generate --source <path>`
|
|
6
6
|
* auto-uploads. Use these commands to upload once and reuse across multiple
|
|
7
7
|
* generation runs, or to inspect processing status.
|
|
8
|
+
*
|
|
9
|
+
* The public CLI command name `source` is preserved for compatibility with
|
|
10
|
+
* existing scripts and agents; internally we call the unified
|
|
11
|
+
* /people/attachments/* endpoint family (see ADR-0034).
|
|
8
12
|
*/
|
|
9
13
|
import type { Command } from "commander";
|
|
10
14
|
export declare function registerSourceCommands(program: Command): void;
|
package/dist/commands/source.js
CHANGED
|
@@ -1,42 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ish source — Upload and inspect
|
|
2
|
+
* ish source — Upload and inspect participant attachments used as generation inputs.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Attachments (transcripts, audio, images, PDFs) are inputs to `ish person
|
|
5
5
|
* generate`. For one-shot generation, `profile generate --source <path>`
|
|
6
6
|
* auto-uploads. Use these commands to upload once and reuse across multiple
|
|
7
7
|
* generation runs, or to inspect processing status.
|
|
8
|
+
*
|
|
9
|
+
* The public CLI command name `source` is preserved for compatibility with
|
|
10
|
+
* existing scripts and agents; internally we call the unified
|
|
11
|
+
* /people/attachments/* endpoint family (see ADR-0034).
|
|
8
12
|
*/
|
|
9
13
|
import { withClient, resolveWorkspace } from "../lib/command-helpers.js";
|
|
10
14
|
import { resolveId, tagAlias, ALIAS_PREFIX } from "../lib/alias-store.js";
|
|
11
15
|
import { normalizeEnumValue } from "../lib/enums.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
16
|
+
import { formatAttachment, output } from "../lib/output.js";
|
|
17
|
+
import { inferAttachmentKind, uploadAndProcessAttachment, } from "../lib/profile-sources.js";
|
|
14
18
|
const VALID_KINDS = ["text_file", "audio", "image"];
|
|
15
19
|
export function registerSourceCommands(program) {
|
|
16
20
|
const source = program
|
|
17
21
|
.command("source")
|
|
18
|
-
.description("Upload and inspect
|
|
22
|
+
.description("Upload and inspect participant attachments used as generation inputs (transcripts, audio, images)")
|
|
19
23
|
.addHelpText("after", `
|
|
20
|
-
A source is an input to \`ish
|
|
21
|
-
\`source upload\` when you want to reuse the
|
|
22
|
-
otherwise pass a local path directly
|
|
24
|
+
A source — internally a participant attachment — is an input to \`ish person generate\`:
|
|
25
|
+
transcript, audio, image, or PDF. Use \`source upload\` when you want to reuse the
|
|
26
|
+
same attachment across multiple generation runs; otherwise pass a local path directly
|
|
27
|
+
to \`profile generate --source\` and it auto-uploads.
|
|
23
28
|
|
|
24
29
|
Concept pages: ish docs get-page concepts/source
|
|
25
30
|
ish docs get-page concepts/profile`);
|
|
26
31
|
source
|
|
27
32
|
.command("upload")
|
|
28
|
-
.description("Upload a file as
|
|
33
|
+
.description("Upload a file as a participant attachment and wait for processing")
|
|
29
34
|
.argument("<file>", "Local file path (transcript, audio, image, PDF, etc.)")
|
|
30
35
|
.option("--workspace <id>", "Workspace (product) ID; falls back to active workspace")
|
|
31
|
-
.option("--kind <kind>", "
|
|
32
|
-
.option("--description <text>", "Context note attached to the
|
|
33
|
-
.option("--diarize", "
|
|
36
|
+
.option("--kind <kind>", "Attachment kind: text_file | audio | image (auto-detected if omitted; hyphen/underscore variants accepted)")
|
|
37
|
+
.option("--description <text>", "Context note attached to the file (max 500 chars)")
|
|
38
|
+
.option("--diarize", "Accepted for backward compat; audio diarization is the worker default and the flag is ignored.")
|
|
34
39
|
.option("--no-wait", "Don't poll until terminal status — return after confirm")
|
|
35
40
|
.option("--timeout <seconds>", "Poll timeout in seconds (default 300)", "300")
|
|
36
41
|
.addHelpText("after", `
|
|
37
42
|
Examples:
|
|
38
43
|
$ ish source upload ./transcript.txt
|
|
39
|
-
$ ish source upload ./call.mp3 --
|
|
44
|
+
$ ish source upload ./call.mp3 --description "Q3 churn interview"
|
|
40
45
|
$ ish source upload ./screenshot.png --kind image --no-wait`)
|
|
41
46
|
.action(async (file, opts, cmd) => {
|
|
42
47
|
await withClient(cmd, async (client, globals) => {
|
|
@@ -50,10 +55,10 @@ Examples:
|
|
|
50
55
|
kind = normalized;
|
|
51
56
|
}
|
|
52
57
|
else {
|
|
53
|
-
kind =
|
|
58
|
+
kind = inferAttachmentKind(file);
|
|
54
59
|
}
|
|
55
60
|
const timeoutMs = Math.max(1, parseInt(opts.timeout, 10)) * 1000;
|
|
56
|
-
const
|
|
61
|
+
const attachment = await uploadAndProcessAttachment(client, {
|
|
57
62
|
productId,
|
|
58
63
|
filePath: file,
|
|
59
64
|
kind,
|
|
@@ -63,39 +68,39 @@ Examples:
|
|
|
63
68
|
timeoutMs,
|
|
64
69
|
quiet: globals.quiet,
|
|
65
70
|
});
|
|
66
|
-
|
|
71
|
+
formatAttachment(attachment, globals.json);
|
|
67
72
|
});
|
|
68
73
|
});
|
|
69
74
|
source
|
|
70
75
|
.command("get")
|
|
71
|
-
.description("Get
|
|
72
|
-
.argument("<id>", "
|
|
73
|
-
.addHelpText("after", "\nExamples:\n $ ish source get
|
|
76
|
+
.description("Get a participant attachment's current status")
|
|
77
|
+
.argument("<id>", "Attachment ID or alias")
|
|
78
|
+
.addHelpText("after", "\nExamples:\n $ ish source get ps-3a4")
|
|
74
79
|
.action(async (id, _opts, cmd) => {
|
|
75
80
|
await withClient(cmd, async (client, globals) => {
|
|
76
|
-
const
|
|
77
|
-
|
|
81
|
+
const attachment = await client.get(`/people/attachments/${resolveId(id)}`);
|
|
82
|
+
formatAttachment(attachment, globals.json);
|
|
78
83
|
});
|
|
79
84
|
});
|
|
80
85
|
source
|
|
81
86
|
.command("delete")
|
|
82
|
-
.description("Delete
|
|
83
|
-
.argument("<id>", "
|
|
87
|
+
.description("Delete a participant attachment plus its uploaded file")
|
|
88
|
+
.argument("<id>", "Attachment ID or alias")
|
|
84
89
|
.addHelpText("after", `
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
The backend ref-counts attachment deletes: the file row + storage object are
|
|
91
|
+
removed only when no profile mappings remain. Profiles already generated from
|
|
92
|
+
this attachment keep their seed mapping until they themselves are deleted.
|
|
88
93
|
|
|
89
94
|
Examples:
|
|
90
|
-
$ ish source delete
|
|
95
|
+
$ ish source delete ps-3a4`)
|
|
91
96
|
.action(async (id, _opts, cmd) => {
|
|
92
97
|
await withClient(cmd, async (client, globals) => {
|
|
93
98
|
const rid = resolveId(id);
|
|
94
|
-
await client.del(`/
|
|
99
|
+
await client.del(`/people/attachments/${rid}`);
|
|
95
100
|
output({
|
|
96
101
|
id: rid,
|
|
97
|
-
alias: tagAlias(ALIAS_PREFIX.
|
|
98
|
-
message: "
|
|
102
|
+
alias: tagAlias(ALIAS_PREFIX.personSource, rid),
|
|
103
|
+
message: "Attachment deleted",
|
|
99
104
|
}, globals.json, { writePath: true });
|
|
100
105
|
});
|
|
101
106
|
});
|
|
@@ -19,7 +19,7 @@ export interface KeyInsight {
|
|
|
19
19
|
title: string;
|
|
20
20
|
description: string;
|
|
21
21
|
category: "friction" | "confusion" | "blocker" | "observation" | "positive";
|
|
22
|
-
|
|
22
|
+
participant_count: number;
|
|
23
23
|
iteration_labels: string[];
|
|
24
24
|
is_discarded: boolean;
|
|
25
25
|
interaction_ids: string[];
|
|
@@ -51,12 +51,12 @@ async function pollAnalysisUntilDone(client, opts) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
function formatKeyInsightsTable(insights) {
|
|
54
|
-
printTable(["#", "CATEGORY", "
|
|
54
|
+
printTable(["#", "CATEGORY", "PARTICIPANTS", "TITLE"], insights
|
|
55
55
|
.filter((i) => !i.is_discarded)
|
|
56
56
|
.map((i) => [
|
|
57
57
|
String(i.sequence),
|
|
58
58
|
i.category,
|
|
59
|
-
String(i.
|
|
59
|
+
String(i.participant_count),
|
|
60
60
|
i.title,
|
|
61
61
|
]));
|
|
62
62
|
}
|
|
@@ -95,7 +95,7 @@ Examples:
|
|
|
95
95
|
|
|
96
96
|
Prerequisites (enforced server-side):
|
|
97
97
|
- Study modality is one of: interactive, video, audio, text, image, document
|
|
98
|
-
- At least 5
|
|
98
|
+
- At least 5 participants with completed interactions
|
|
99
99
|
|
|
100
100
|
Read prior runs:
|
|
101
101
|
$ ish study insights <study-id>`)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish study participant — Inspect and manage participants (low-level; usually
|
|
3
|
+
* created via `ish study run`).
|
|
4
|
+
*
|
|
5
|
+
* Default action: `ish study participant <id>` shows participant details and results.
|
|
6
|
+
*/
|
|
7
|
+
import type { Command } from "commander";
|
|
8
|
+
export declare function attachStudyParticipantCommands(study: Command): void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ish study
|
|
2
|
+
* ish study participant — Inspect and manage participants (low-level; usually
|
|
3
3
|
* created via `ish study run`).
|
|
4
4
|
*
|
|
5
|
-
* Default action: `ish study
|
|
5
|
+
* Default action: `ish study participant <id>` shows participant details and results.
|
|
6
6
|
*/
|
|
7
7
|
import { withClient, readJsonFileOrStdin, resolveWorkspace } from "../lib/command-helpers.js";
|
|
8
8
|
import { resolveId, tagAlias, ALIAS_PREFIX } from "../lib/alias-store.js";
|
|
9
|
-
import {
|
|
9
|
+
import { formatParticipantDetail, buildParticipantSummary, output } from "../lib/output.js";
|
|
10
10
|
/** Pick the latest iteration on a study (highest order_index, falling back to last). */
|
|
11
11
|
async function latestIterationForStudy(client, studyId) {
|
|
12
12
|
const study = await client.get(`/studies/${studyId}`);
|
|
@@ -18,12 +18,12 @@ async function latestIterationForStudy(client, studyId) {
|
|
|
18
18
|
return sorted[0].id;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Walk a parsed JSON body and resolve any alias-shaped strings (
|
|
21
|
+
* Walk a parsed JSON body and resolve any alias-shaped strings (p-..., i-..., s-...)
|
|
22
22
|
* appearing in well-known id fields. Pure client-side: avoids a backend round-trip
|
|
23
23
|
* when the body uses local aliases that the backend wouldn't recognise.
|
|
24
24
|
*/
|
|
25
|
-
const ALIAS_FIELDS = new Set(["
|
|
26
|
-
const ALIAS_RE = /^(
|
|
25
|
+
const ALIAS_FIELDS = new Set(["person_id", "iteration_id", "study_id"]);
|
|
26
|
+
const ALIAS_RE = /^(p|i|s)-[0-9a-f]{3,}$/i;
|
|
27
27
|
function prewalkAliases(value) {
|
|
28
28
|
if (Array.isArray(value)) {
|
|
29
29
|
return value.map(prewalkAliases);
|
|
@@ -47,20 +47,20 @@ function prewalkAliases(value) {
|
|
|
47
47
|
}
|
|
48
48
|
return value;
|
|
49
49
|
}
|
|
50
|
-
export function
|
|
51
|
-
const
|
|
52
|
-
.command("
|
|
53
|
-
.description("Inspect or manage
|
|
54
|
-
.argument("[id]", "
|
|
55
|
-
.option("--workspace <id>", "Workspace ID; accepted for consistency (workspace is inferred from the
|
|
50
|
+
export function attachStudyParticipantCommands(study) {
|
|
51
|
+
const participant = study
|
|
52
|
+
.command("participant")
|
|
53
|
+
.description("Inspect or manage participants (low-level; usually created via `study run`)")
|
|
54
|
+
.argument("[id]", "Participant ID — pass directly to view participant details and results")
|
|
55
|
+
.option("--workspace <id>", "Workspace ID; accepted for consistency (workspace is inferred from the participant)")
|
|
56
56
|
.option("--summary", "Lean projection: alias + status + sentiment + comment + error_message. Drops the action timeline / interactions array.")
|
|
57
57
|
.addHelpText("after", `
|
|
58
58
|
Examples:
|
|
59
|
-
$ ish study
|
|
60
|
-
$ ish study
|
|
61
|
-
$ ish study
|
|
62
|
-
$ ish study
|
|
63
|
-
$ ish study
|
|
59
|
+
$ ish study participant pt-d4e # show participant details
|
|
60
|
+
$ ish study participant pt-d4e --summary --json # headline only (sentiment + comment)
|
|
61
|
+
$ ish study participant create --iteration <id> --person <id>
|
|
62
|
+
$ ish study participant batch-create --iteration <id> --file participants.json
|
|
63
|
+
$ ish study participant delete pt-d4e
|
|
64
64
|
|
|
65
65
|
Tips:
|
|
66
66
|
Use \`--get <path>\` to grab one value (e.g. \`--get sentiment\`),
|
|
@@ -72,27 +72,27 @@ Tips:
|
|
|
72
72
|
await withClient(cmd, async (client, globals) => {
|
|
73
73
|
if (opts.workspace)
|
|
74
74
|
resolveWorkspace(opts.workspace);
|
|
75
|
-
const data = await client.get(`/
|
|
75
|
+
const data = await client.get(`/participants/${resolveId(id)}`);
|
|
76
76
|
const result = data;
|
|
77
77
|
if (result.id)
|
|
78
|
-
result.alias = tagAlias(ALIAS_PREFIX.
|
|
78
|
+
result.alias = tagAlias(ALIAS_PREFIX.participant, String(result.id));
|
|
79
79
|
if (opts.summary) {
|
|
80
|
-
output(
|
|
80
|
+
output(buildParticipantSummary(result), globals.json, { preProjected: true });
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
formatParticipantDetail(result, globals.json);
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
|
-
|
|
86
|
+
participant
|
|
87
87
|
.command("create")
|
|
88
|
-
.description("Create a
|
|
88
|
+
.description("Create a participant (low-level)")
|
|
89
89
|
.option("--iteration <id>", "Iteration ID (or use --study to pick the latest iteration on a study)")
|
|
90
90
|
.option("--study <id>", "Study ID; resolves to the latest iteration on that study (alternative to --iteration)")
|
|
91
|
-
.requiredOption("--
|
|
91
|
+
.requiredOption("--person <id>", "Participant profile ID")
|
|
92
92
|
.option("--language <lang>", "Language code (e.g. en, sv)")
|
|
93
93
|
.option("--platform <platform>", "Platform (browser, android, figma, code)")
|
|
94
|
-
.option("--
|
|
95
|
-
.addHelpText("after", "\nExamples:\n $ ish study
|
|
94
|
+
.option("--participant-type <type>", "Participant type (ai, human)", "ai")
|
|
95
|
+
.addHelpText("after", "\nExamples:\n $ ish study participant create --iteration <id> --person <id>\n $ ish study participant create --study s-XXX --person p-XXX\n $ ish study participant create --iteration <id> --person <id> --platform android --json")
|
|
96
96
|
.action(async (opts, cmd) => {
|
|
97
97
|
await withClient(cmd, async (client, globals) => {
|
|
98
98
|
if (!opts.iteration && !opts.study) {
|
|
@@ -105,63 +105,63 @@ Tips:
|
|
|
105
105
|
? resolveId(opts.iteration)
|
|
106
106
|
: await latestIterationForStudy(client, resolveId(opts.study));
|
|
107
107
|
const body = {
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
person_id: resolveId(opts.person),
|
|
109
|
+
participant_type: opts.participantType,
|
|
110
110
|
...(opts.language && { language: opts.language }),
|
|
111
111
|
...(opts.platform && { platform: opts.platform }),
|
|
112
112
|
};
|
|
113
|
-
const data = await client.post(`/iterations/${iterationId}/
|
|
113
|
+
const data = await client.post(`/iterations/${iterationId}/participants`, body);
|
|
114
114
|
const result = data;
|
|
115
115
|
if (result.id)
|
|
116
|
-
result.alias = tagAlias(ALIAS_PREFIX.
|
|
116
|
+
result.alias = tagAlias(ALIAS_PREFIX.participant, String(result.id));
|
|
117
117
|
output(result, globals.json);
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
|
-
|
|
120
|
+
participant
|
|
121
121
|
.command("batch-create")
|
|
122
|
-
.description("Create multiple
|
|
122
|
+
.description("Create multiple participants (low-level)")
|
|
123
123
|
.requiredOption("--iteration <id>", "Iteration ID")
|
|
124
|
-
.option("--file <path>", "JSON file with
|
|
125
|
-
.option("--
|
|
126
|
-
.addHelpText("after", "\nExamples:\n $ ish study
|
|
124
|
+
.option("--file <path>", "JSON file with participants array (alternative to --persons)")
|
|
125
|
+
.option("--persons <ids>", "Comma-separated profile IDs/aliases; shortcut for --file with one participant per profile (mutually exclusive with --file)")
|
|
126
|
+
.addHelpText("after", "\nExamples:\n $ ish study participant batch-create --iteration <id> --file participants.json\n $ ish study participant batch-create --iteration <id> --persons p-eba,p-289,p-913\n\n Expected JSON: [{ \"person_id\": \"<id>\", \"platform\": \"browser\" }, ...]\n Profile-id strings inside --file (person_id, iteration_id, study_id) are resolved client-side via the local alias store before submitting.")
|
|
127
127
|
.action(async (opts, cmd) => {
|
|
128
128
|
await withClient(cmd, async (client, globals) => {
|
|
129
|
-
if (!opts.file && !opts.
|
|
130
|
-
throw new Error("Provide --file <path> or --
|
|
129
|
+
if (!opts.file && !opts.persons) {
|
|
130
|
+
throw new Error("Provide --file <path> or --persons <ids>.");
|
|
131
131
|
}
|
|
132
|
-
if (opts.file && opts.
|
|
133
|
-
throw new Error("Pass either --file or --
|
|
132
|
+
if (opts.file && opts.persons) {
|
|
133
|
+
throw new Error("Pass either --file or --persons, not both.");
|
|
134
134
|
}
|
|
135
135
|
const iterationId = resolveId(opts.iteration);
|
|
136
136
|
let body;
|
|
137
|
-
if (opts.
|
|
138
|
-
const ids = opts.
|
|
137
|
+
if (opts.persons) {
|
|
138
|
+
const ids = opts.persons
|
|
139
139
|
.split(",")
|
|
140
140
|
.map((s) => s.trim())
|
|
141
141
|
.filter(Boolean)
|
|
142
142
|
.map((s) => resolveId(s));
|
|
143
143
|
if (ids.length === 0) {
|
|
144
|
-
throw new Error("--
|
|
144
|
+
throw new Error("--persons must contain at least one profile id/alias.");
|
|
145
145
|
}
|
|
146
|
-
body = {
|
|
146
|
+
body = { participants: ids.map((pid) => ({ person_id: pid, participant_type: "ai" })) };
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
149
149
|
const parsed = await readJsonFileOrStdin(opts.file);
|
|
150
150
|
body = prewalkAliases(parsed);
|
|
151
151
|
}
|
|
152
|
-
const data = await client.post(`/iterations/${iterationId}/
|
|
152
|
+
const data = await client.post(`/iterations/${iterationId}/participants/batch`, body);
|
|
153
153
|
output(data, globals.json);
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
|
-
|
|
156
|
+
participant
|
|
157
157
|
.command("delete")
|
|
158
|
-
.description("Delete a
|
|
159
|
-
.argument("<id>", "
|
|
160
|
-
.addHelpText("after", "\nExamples:\n $ ish study
|
|
158
|
+
.description("Delete a participant")
|
|
159
|
+
.argument("<id>", "Participant ID")
|
|
160
|
+
.addHelpText("after", "\nExamples:\n $ ish study participant delete <id>")
|
|
161
161
|
.action(async (id, _opts, cmd) => {
|
|
162
162
|
await withClient(cmd, async (client, globals) => {
|
|
163
|
-
await client.del(`/
|
|
164
|
-
output({ message: "
|
|
163
|
+
await client.del(`/participants/${resolveId(id)}`);
|
|
164
|
+
output({ message: "Participant deleted" }, globals.json);
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ish study run — Run, monitor, and cancel simulations of a study.
|
|
3
3
|
*
|
|
4
|
-
* `ish study run` creates
|
|
4
|
+
* `ish study run` creates participants for the latest (or specified) iteration
|
|
5
5
|
* and dispatches simulations. Iterations are created separately via
|
|
6
6
|
* `ish iteration create`, which carries the URL/content details.
|
|
7
7
|
*
|
|
@@ -13,7 +13,7 @@ import type { Command } from "commander";
|
|
|
13
13
|
* produce a structured envelope (`error_code: "wait_timeout"`, exit 5
|
|
14
14
|
* transient) distinct from the generic timeout/network/server errors
|
|
15
15
|
* that the api-client wrapper produces. Carries the in-flight progress
|
|
16
|
-
* (
|
|
16
|
+
* (participants done / total) so `study wait` always emits final state JSON
|
|
17
17
|
* even when it bails on the timer.
|
|
18
18
|
*/
|
|
19
19
|
export declare class WaitTimeoutError extends Error {
|
|
@@ -24,7 +24,7 @@ export declare class WaitTimeoutError extends Error {
|
|
|
24
24
|
done: number;
|
|
25
25
|
total: number;
|
|
26
26
|
pending: number;
|
|
27
|
-
rows:
|
|
27
|
+
rows: ParticipantStatusRow[];
|
|
28
28
|
};
|
|
29
29
|
readonly error_code = "wait_timeout";
|
|
30
30
|
readonly retryable = true;
|
|
@@ -35,13 +35,13 @@ export declare class WaitTimeoutError extends Error {
|
|
|
35
35
|
done: number;
|
|
36
36
|
total: number;
|
|
37
37
|
pending: number;
|
|
38
|
-
rows:
|
|
38
|
+
rows: ParticipantStatusRow[];
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
interface
|
|
41
|
+
interface ParticipantStatusRow {
|
|
42
42
|
id: string;
|
|
43
43
|
status: string;
|
|
44
|
-
|
|
44
|
+
participant_name: string;
|
|
45
45
|
interaction_count: number;
|
|
46
46
|
error_message?: string;
|
|
47
47
|
}
|