@kosuke-ai/kosuke 1.0.47 → 1.0.49
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/CHANGELOG.md +11 -0
- package/README.md +6 -2
- package/dist/commands/skill-runs.js +30 -11
- package/dist/generated/commands/deploys/queue-update.js +1 -1
- package/dist/generated/commands/deploys/start.js +1 -1
- package/dist/generated/commands/files/set--personal.js +1 -1
- package/dist/generated/commands/files/set.js +1 -1
- package/dist/generated/commands/index.js +111 -107
- package/dist/generated/commands/prototypes/create.js +1 -1
- package/dist/generated/commands/sensei/threads-create.js +35 -0
- package/dist/generated/commands/sessions/create.js +1 -1
- package/dist/generated/commands/sessions/env-sync.js +21 -0
- package/dist/generated/commands/sessions/prompt-update.js +1 -1
- package/dist/generated/commands/sessions/qa-set.js +1 -1
- package/dist/generated/commands/sessions/queue-update.js +1 -1
- package/dist/generated/commands/sessions/send.js +1 -1
- package/dist/generated/commands/tasks/create.js +1 -1
- package/dist/program.js +1 -4
- package/dist/runtime/attach.js +13 -51
- package/openapi.json +145 -11
- package/package.json +4 -5
- package/dist/runtime/aliases.js +0 -508
- package/dist/runtime/former-commands.js +0 -89
package/dist/runtime/attach.js
CHANGED
|
@@ -13,16 +13,13 @@ export function attachCommand(root, forms, runtime, { reachable = () => true } =
|
|
|
13
13
|
const [only] = forms;
|
|
14
14
|
if (forms.length === 1 && !only.variant) {
|
|
15
15
|
// A hidden operation sits beside a hand-written command in a visible group.
|
|
16
|
-
return attachLeaf(root, only.command, only, runtime,
|
|
17
|
-
hidden: only.hidden === true,
|
|
18
|
-
groupsHidden: false,
|
|
19
|
-
});
|
|
16
|
+
return attachLeaf(root, only.command, only, runtime, only.hidden === true);
|
|
20
17
|
}
|
|
21
18
|
return attachForms(root, forms, runtime, reachable);
|
|
22
19
|
}
|
|
23
|
-
/** A
|
|
24
|
-
|
|
25
|
-
const parent = groupFor(root, path.slice(0, -1)
|
|
20
|
+
/** A leaf for an operation with a single form. */
|
|
21
|
+
function attachLeaf(root, path, operation, runtime, hidden) {
|
|
22
|
+
const parent = groupFor(root, path.slice(0, -1));
|
|
26
23
|
const leaf = parent.command(path[path.length - 1], { hidden });
|
|
27
24
|
leaf.description(operation.summary);
|
|
28
25
|
for (const name of operation.pathParams) {
|
|
@@ -31,25 +28,13 @@ export function attachLeaf(root, path, operation, runtime, { hidden, groupsHidde
|
|
|
31
28
|
const fields = fieldsOf(operation);
|
|
32
29
|
for (const field of fields)
|
|
33
30
|
addFieldOptions(leaf, field, field.description ?? '');
|
|
34
|
-
for (const [former, current] of Object.entries(formerFlags)) {
|
|
35
|
-
const field = fields.find((candidate) => candidate.flag === current);
|
|
36
|
-
if (!field) {
|
|
37
|
-
throw new Error(`kosuke ${path.join(' ')}: --${former} names --${current}, not a flag.`);
|
|
38
|
-
}
|
|
39
|
-
const option = new Option(`--${former} <value>`).hideHelp();
|
|
40
|
-
// Repeated as it was, `--project-ids a --project-ids b`, or one JSON list.
|
|
41
|
-
if (field.repeated)
|
|
42
|
-
option.argParser(collect).default(undefined);
|
|
43
|
-
leaf.addOption(option);
|
|
44
|
-
}
|
|
45
31
|
addBodyOptions(leaf, operation);
|
|
46
32
|
leaf.action(async (...args) => {
|
|
47
33
|
const options = args[args.length - 2];
|
|
48
34
|
const positionals = args.slice(0, operation.pathParams.length);
|
|
49
35
|
try {
|
|
50
36
|
checkFileFlags(options, fields.filter((field) => field.file).map((field) => field.flag));
|
|
51
|
-
// `personal` where a workspace id goes
|
|
52
|
-
// leaf runs a personal form by its old path, which takes no workspace id at all.
|
|
37
|
+
// Refuse `personal` where a workspace id goes on a command with no personal form.
|
|
53
38
|
if (!operation.variant)
|
|
54
39
|
selectForm([operation], { first: positionals[0], flags: {} });
|
|
55
40
|
}
|
|
@@ -58,19 +43,13 @@ export function attachLeaf(root, path, operation, runtime, { hidden, groupsHidde
|
|
|
58
43
|
leaf.error(error.message);
|
|
59
44
|
throw error;
|
|
60
45
|
}
|
|
61
|
-
const values = Object.fromEntries(fields.map((field) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
: undefined;
|
|
66
|
-
return [
|
|
67
|
-
field.name,
|
|
68
|
-
options[optionKey(field.flag)] ?? fileValue(field, options) ?? formerValue,
|
|
69
|
-
];
|
|
70
|
-
}));
|
|
46
|
+
const values = Object.fromEntries(fields.map((field) => [
|
|
47
|
+
field.name,
|
|
48
|
+
options[optionKey(field.flag)] ?? fileValue(field, options),
|
|
49
|
+
]));
|
|
71
50
|
await run(leaf, runtime, operation, {
|
|
72
51
|
pathParams: Object.fromEntries(operation.pathParams.map((name, i) => [name, positionals[i]])),
|
|
73
|
-
fields:
|
|
52
|
+
fields: values,
|
|
74
53
|
wholeBody: readWholeBody({
|
|
75
54
|
body: options.body,
|
|
76
55
|
bodyFile: options.bodyFile,
|
|
@@ -242,22 +221,6 @@ function parseForForm(field, raw) {
|
|
|
242
221
|
return raw === 'true';
|
|
243
222
|
return field.repeated ? [raw] : raw;
|
|
244
223
|
}
|
|
245
|
-
/** The value of a renamed flag, by the rule its field had: one JSON list, or the values given. */
|
|
246
|
-
function formerValueOf(field, raw) {
|
|
247
|
-
if (Array.isArray(raw))
|
|
248
|
-
return raw.flatMap((value) => formerValueOf(field, value));
|
|
249
|
-
if (typeof raw !== 'string')
|
|
250
|
-
return raw;
|
|
251
|
-
if (!field.repeated)
|
|
252
|
-
return field.kind === 'json' ? jsonParser(raw) : raw;
|
|
253
|
-
try {
|
|
254
|
-
const parsed = JSON.parse(raw);
|
|
255
|
-
return Array.isArray(parsed) ? parsed : [raw];
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
return [raw];
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
224
|
function addFieldOptions(leaf, field, description) {
|
|
262
225
|
leaf.addOption(optionFor({ ...field, description }));
|
|
263
226
|
if (field.file) {
|
|
@@ -279,13 +242,12 @@ function addBodyOptions(leaf, operation) {
|
|
|
279
242
|
leaf.option('--attach <path>', 'Attach a file; repeat for several. Sends the request as multipart.', collect, []);
|
|
280
243
|
}
|
|
281
244
|
}
|
|
282
|
-
/**
|
|
283
|
-
export function groupFor(root, path
|
|
245
|
+
/** Find or create the command groups on the way to a leaf. */
|
|
246
|
+
export function groupFor(root, path) {
|
|
284
247
|
let current = root;
|
|
285
248
|
for (const segment of path) {
|
|
286
249
|
const existing = current.commands.find((command) => command.name() === segment);
|
|
287
|
-
current =
|
|
288
|
-
existing ?? current.command(segment, { hidden }).description(`${humanize(segment)} commands`);
|
|
250
|
+
current = existing ?? current.command(segment).description(`${humanize(segment)} commands`);
|
|
289
251
|
}
|
|
290
252
|
return current;
|
|
291
253
|
}
|
package/openapi.json
CHANGED
|
@@ -6056,7 +6056,7 @@
|
|
|
6056
6056
|
"tags": ["files"],
|
|
6057
6057
|
"x-kosuke-command": ["files", "set"],
|
|
6058
6058
|
"x-kosuke-attachments": true,
|
|
6059
|
-
"x-kosuke-attachment-max-files":
|
|
6059
|
+
"x-kosuke-attachment-max-files": 20,
|
|
6060
6060
|
"x-kosuke-attachment-max-bytes": 26214400,
|
|
6061
6061
|
"x-kosuke-sandbox-token": true,
|
|
6062
6062
|
"x-kosuke-cron-token": true,
|
|
@@ -8358,7 +8358,7 @@
|
|
|
8358
8358
|
"tags": ["tasks"],
|
|
8359
8359
|
"x-kosuke-command": ["tasks", "create"],
|
|
8360
8360
|
"x-kosuke-attachments": true,
|
|
8361
|
-
"x-kosuke-attachment-max-files":
|
|
8361
|
+
"x-kosuke-attachment-max-files": 20,
|
|
8362
8362
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
8363
8363
|
"x-kosuke-sandbox-token": true,
|
|
8364
8364
|
"x-kosuke-cron-token": true,
|
|
@@ -11314,7 +11314,7 @@
|
|
|
11314
11314
|
"tags": ["sessions"],
|
|
11315
11315
|
"x-kosuke-command": ["sessions", "create"],
|
|
11316
11316
|
"x-kosuke-attachments": true,
|
|
11317
|
-
"x-kosuke-attachment-max-files":
|
|
11317
|
+
"x-kosuke-attachment-max-files": 20,
|
|
11318
11318
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
11319
11319
|
"x-kosuke-sandbox-token": true,
|
|
11320
11320
|
"x-kosuke-cron-token": true,
|
|
@@ -12332,6 +12332,72 @@
|
|
|
12332
12332
|
}
|
|
12333
12333
|
}
|
|
12334
12334
|
},
|
|
12335
|
+
"/api/projects/{projectId}/chat-sessions/{sessionId}/env-sync": {
|
|
12336
|
+
"post": {
|
|
12337
|
+
"operationId": "sessionsEnvSync",
|
|
12338
|
+
"summary": "Deliver the project's saved environment variables, with its services and setup commands, to a chat session's running sandbox and restart its setup and services, so a variable added after the sandbox booted reaches them. Answers status synced with the variable names, never their values, once the services answer again, or status running when setup outlasts ten minutes and the sync goes on. Refused, never queued: 409 TURN_RUNNING while a turn runs, SANDBOX_NOT_RUNNING when the sandbox is not up (it is not woken), SANDBOX_PROVISIONING or SANDBOX_NOT_READY while it is created or boots, APP_STACK_STARTING during an App stack start and SANDBOX_BUSY while another update changes it; 422 ENV_SYNC_FAILED when setup or a service did not come back.",
|
|
12339
|
+
"tags": ["sessions"],
|
|
12340
|
+
"x-kosuke-command": ["sessions", "env", "sync"],
|
|
12341
|
+
"x-kosuke-answer-timeout-ms": 660000,
|
|
12342
|
+
"x-kosuke-sandbox-token": true,
|
|
12343
|
+
"x-kosuke-cron-token": true,
|
|
12344
|
+
"x-kosuke-sensei-token": true,
|
|
12345
|
+
"parameters": [
|
|
12346
|
+
{
|
|
12347
|
+
"name": "projectId",
|
|
12348
|
+
"in": "path",
|
|
12349
|
+
"required": true,
|
|
12350
|
+
"schema": {
|
|
12351
|
+
"type": "string",
|
|
12352
|
+
"format": "uuid",
|
|
12353
|
+
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
|
|
12354
|
+
}
|
|
12355
|
+
},
|
|
12356
|
+
{
|
|
12357
|
+
"name": "sessionId",
|
|
12358
|
+
"in": "path",
|
|
12359
|
+
"required": true,
|
|
12360
|
+
"schema": {
|
|
12361
|
+
"type": "string",
|
|
12362
|
+
"format": "uuid",
|
|
12363
|
+
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
|
|
12364
|
+
}
|
|
12365
|
+
}
|
|
12366
|
+
],
|
|
12367
|
+
"responses": {
|
|
12368
|
+
"2XX": {
|
|
12369
|
+
"description": "Success envelope",
|
|
12370
|
+
"content": {
|
|
12371
|
+
"application/json": {
|
|
12372
|
+
"schema": {
|
|
12373
|
+
"$ref": "#/components/schemas/ApiSuccess"
|
|
12374
|
+
}
|
|
12375
|
+
}
|
|
12376
|
+
}
|
|
12377
|
+
},
|
|
12378
|
+
"4XX": {
|
|
12379
|
+
"description": "Error envelope",
|
|
12380
|
+
"content": {
|
|
12381
|
+
"application/json": {
|
|
12382
|
+
"schema": {
|
|
12383
|
+
"$ref": "#/components/schemas/ApiError"
|
|
12384
|
+
}
|
|
12385
|
+
}
|
|
12386
|
+
}
|
|
12387
|
+
},
|
|
12388
|
+
"5XX": {
|
|
12389
|
+
"description": "Error envelope",
|
|
12390
|
+
"content": {
|
|
12391
|
+
"application/json": {
|
|
12392
|
+
"schema": {
|
|
12393
|
+
"$ref": "#/components/schemas/ApiError"
|
|
12394
|
+
}
|
|
12395
|
+
}
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
}
|
|
12400
|
+
},
|
|
12335
12401
|
"/api/projects/{projectId}/chat-sessions/{sessionId}/go-ahead": {
|
|
12336
12402
|
"put": {
|
|
12337
12403
|
"operationId": "sessionsGoAheadSet",
|
|
@@ -12573,7 +12639,7 @@
|
|
|
12573
12639
|
"tags": ["sessions"],
|
|
12574
12640
|
"x-kosuke-command": ["sessions", "send"],
|
|
12575
12641
|
"x-kosuke-attachments": true,
|
|
12576
|
-
"x-kosuke-attachment-max-files":
|
|
12642
|
+
"x-kosuke-attachment-max-files": 20,
|
|
12577
12643
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
12578
12644
|
"x-kosuke-sandbox-token": true,
|
|
12579
12645
|
"x-kosuke-cron-token": true,
|
|
@@ -14364,7 +14430,7 @@
|
|
|
14364
14430
|
"tags": ["sessions"],
|
|
14365
14431
|
"x-kosuke-command": ["sessions", "prompt", "update"],
|
|
14366
14432
|
"x-kosuke-attachments": true,
|
|
14367
|
-
"x-kosuke-attachment-max-files":
|
|
14433
|
+
"x-kosuke-attachment-max-files": 20,
|
|
14368
14434
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
14369
14435
|
"x-kosuke-sandbox-token": true,
|
|
14370
14436
|
"x-kosuke-cron-token": true,
|
|
@@ -14472,7 +14538,7 @@
|
|
|
14472
14538
|
"/api/projects/{projectId}/chat-sessions/{sessionId}/qa-passed": {
|
|
14473
14539
|
"put": {
|
|
14474
14540
|
"operationId": "sessionsQaSet",
|
|
14475
|
-
"summary": "Record the commit QA passed on as the session's qaPassedSha: a member's override
|
|
14541
|
+
"summary": "Record the commit QA passed on as the session's qaPassedSha: a member's override. `sessions qa runs finish` records it when the whole checklist passed. A pull request is done when qaPassedSha equals its head.",
|
|
14476
14542
|
"tags": ["sessions"],
|
|
14477
14543
|
"x-kosuke-command": ["sessions", "qa", "set"],
|
|
14478
14544
|
"x-kosuke-sandbox-token": true,
|
|
@@ -15580,7 +15646,7 @@
|
|
|
15580
15646
|
"tags": ["sessions"],
|
|
15581
15647
|
"x-kosuke-command": ["sessions", "queue", "update"],
|
|
15582
15648
|
"x-kosuke-attachments": true,
|
|
15583
|
-
"x-kosuke-attachment-max-files":
|
|
15649
|
+
"x-kosuke-attachment-max-files": 20,
|
|
15584
15650
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
15585
15651
|
"x-kosuke-sandbox-token": true,
|
|
15586
15652
|
"x-kosuke-cron-token": true,
|
|
@@ -18386,7 +18452,7 @@
|
|
|
18386
18452
|
"tags": ["deploys"],
|
|
18387
18453
|
"x-kosuke-command": ["deploys", "start"],
|
|
18388
18454
|
"x-kosuke-attachments": true,
|
|
18389
|
-
"x-kosuke-attachment-max-files":
|
|
18455
|
+
"x-kosuke-attachment-max-files": 20,
|
|
18390
18456
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
18391
18457
|
"x-kosuke-sandbox-token": true,
|
|
18392
18458
|
"x-kosuke-cron-token": true,
|
|
@@ -18673,7 +18739,7 @@
|
|
|
18673
18739
|
"tags": ["deploys"],
|
|
18674
18740
|
"x-kosuke-command": ["deploys", "queue", "update"],
|
|
18675
18741
|
"x-kosuke-attachments": true,
|
|
18676
|
-
"x-kosuke-attachment-max-files":
|
|
18742
|
+
"x-kosuke-attachment-max-files": 20,
|
|
18677
18743
|
"x-kosuke-attachment-max-bytes": 10485760,
|
|
18678
18744
|
"x-kosuke-sandbox-token": true,
|
|
18679
18745
|
"x-kosuke-cron-token": true,
|
|
@@ -20375,7 +20441,7 @@
|
|
|
20375
20441
|
"tags": ["prototypes"],
|
|
20376
20442
|
"x-kosuke-command": ["prototypes", "create"],
|
|
20377
20443
|
"x-kosuke-attachments": true,
|
|
20378
|
-
"x-kosuke-attachment-max-files":
|
|
20444
|
+
"x-kosuke-attachment-max-files": 20,
|
|
20379
20445
|
"x-kosuke-attachment-max-bytes": 1048576,
|
|
20380
20446
|
"x-kosuke-sandbox-token": true,
|
|
20381
20447
|
"x-kosuke-sensei-token": true,
|
|
@@ -24326,6 +24392,74 @@
|
|
|
24326
24392
|
}
|
|
24327
24393
|
}
|
|
24328
24394
|
},
|
|
24395
|
+
"/api/sensei/thread-create": {
|
|
24396
|
+
"post": {
|
|
24397
|
+
"operationId": "senseiThreadsCreate",
|
|
24398
|
+
"summary": "Open a new thread for the member, at any point of this Turn, on a prompt you write. Only Sensei calls it, for a separate topic that should not go on in this thread. Never for the asks of the member’s message that started this thread: `sensei threads split` does that. The new thread gets its own Turn, which may read Queued, and links back here; nothing of this thread goes with it, so the prompt says all it needs. A Turn opens at most 3 threads, and so do the threads opened from one thread in all. Answers the new thread id and turn id: do not do that work here.",
|
|
24399
|
+
"tags": ["sensei"],
|
|
24400
|
+
"x-kosuke-command": ["sensei", "threads", "create"],
|
|
24401
|
+
"x-kosuke-sandbox-token": true,
|
|
24402
|
+
"x-kosuke-cron-token": true,
|
|
24403
|
+
"x-kosuke-sensei-token": true,
|
|
24404
|
+
"requestBody": {
|
|
24405
|
+
"required": true,
|
|
24406
|
+
"content": {
|
|
24407
|
+
"application/json": {
|
|
24408
|
+
"schema": {
|
|
24409
|
+
"type": "object",
|
|
24410
|
+
"properties": {
|
|
24411
|
+
"prompt": {
|
|
24412
|
+
"type": "string",
|
|
24413
|
+
"minLength": 1,
|
|
24414
|
+
"maxLength": 20000,
|
|
24415
|
+
"description": "What the new thread’s Turn does, whole, in words that stand without this thread: it reads nothing else of it."
|
|
24416
|
+
},
|
|
24417
|
+
"title": {
|
|
24418
|
+
"description": "A short title for the new thread. Without it, the thread is named by the prompt.",
|
|
24419
|
+
"type": "string",
|
|
24420
|
+
"minLength": 1,
|
|
24421
|
+
"maxLength": 200
|
|
24422
|
+
}
|
|
24423
|
+
},
|
|
24424
|
+
"required": ["prompt"]
|
|
24425
|
+
}
|
|
24426
|
+
}
|
|
24427
|
+
}
|
|
24428
|
+
},
|
|
24429
|
+
"responses": {
|
|
24430
|
+
"2XX": {
|
|
24431
|
+
"description": "Success envelope",
|
|
24432
|
+
"content": {
|
|
24433
|
+
"application/json": {
|
|
24434
|
+
"schema": {
|
|
24435
|
+
"$ref": "#/components/schemas/ApiSuccess"
|
|
24436
|
+
}
|
|
24437
|
+
}
|
|
24438
|
+
}
|
|
24439
|
+
},
|
|
24440
|
+
"4XX": {
|
|
24441
|
+
"description": "Error envelope",
|
|
24442
|
+
"content": {
|
|
24443
|
+
"application/json": {
|
|
24444
|
+
"schema": {
|
|
24445
|
+
"$ref": "#/components/schemas/ApiError"
|
|
24446
|
+
}
|
|
24447
|
+
}
|
|
24448
|
+
}
|
|
24449
|
+
},
|
|
24450
|
+
"5XX": {
|
|
24451
|
+
"description": "Error envelope",
|
|
24452
|
+
"content": {
|
|
24453
|
+
"application/json": {
|
|
24454
|
+
"schema": {
|
|
24455
|
+
"$ref": "#/components/schemas/ApiError"
|
|
24456
|
+
}
|
|
24457
|
+
}
|
|
24458
|
+
}
|
|
24459
|
+
}
|
|
24460
|
+
}
|
|
24461
|
+
}
|
|
24462
|
+
},
|
|
24329
24463
|
"/api/sensei/thread-move": {
|
|
24330
24464
|
"post": {
|
|
24331
24465
|
"operationId": "senseiThreadsMove",
|
|
@@ -26480,7 +26614,7 @@
|
|
|
26480
26614
|
"scope": "personal"
|
|
26481
26615
|
},
|
|
26482
26616
|
"x-kosuke-attachments": true,
|
|
26483
|
-
"x-kosuke-attachment-max-files":
|
|
26617
|
+
"x-kosuke-attachment-max-files": 20,
|
|
26484
26618
|
"x-kosuke-attachment-max-bytes": 26214400,
|
|
26485
26619
|
"x-kosuke-sandbox-token": true,
|
|
26486
26620
|
"x-kosuke-cron-token": true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kosuke-ai/kosuke",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.49",
|
|
4
4
|
"description": "Kosuke platform CLI. The client customers install to drive app.kosuke.ai from their own machine.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,11 +40,10 @@
|
|
|
40
40
|
"vitest": "^4.1.12"
|
|
41
41
|
},
|
|
42
42
|
"kosukeRelease": {
|
|
43
|
-
"sourceCommit": "
|
|
44
|
-
"sourceIdentity": "
|
|
43
|
+
"sourceCommit": "2fece35c238337316974c0ce32615f1fbe682fd9",
|
|
44
|
+
"sourceIdentity": "c8c7c09262763d928f5e07b05b602a9a543c07b30c74e04325e18080ab6e3716",
|
|
45
45
|
"fragments": [
|
|
46
|
-
"
|
|
47
|
-
"batch-picks-sw19mf.json"
|
|
46
|
+
"twenty-files-per-message-r7k2qd.json"
|
|
48
47
|
]
|
|
49
48
|
}
|
|
50
49
|
}
|