@uipath/maestro-builder-sdk 6.16.14 → 6.17.1
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 -2
- package/dist/api-index.md +115 -114
- package/dist/api-members.md +295 -291
- package/dist/bindings-v2.d.ts +39 -1
- package/dist/bindings-v2.js +289 -9
- package/dist/bpmn/compile-cli.js +11 -2
- package/dist/bpmn/decompile.js +2 -1
- package/dist/bpmn/registry-fallback.js +9 -7
- package/dist/case/compile-cli.js +16 -6
- package/dist/case/decompile-cli.js +2 -1
- package/dist/case/decompile.js +3 -2
- package/dist/case/merge-cli.js +3 -2
- package/dist/check-cli.js +2 -30
- package/dist/check.js +278 -55
- package/dist/cli-library.d.ts +18 -4
- package/dist/cli-library.js +24 -6
- package/dist/cli-result.d.ts +9 -0
- package/dist/cli-result.js +12 -0
- package/dist/compile-cli.js +82 -86
- package/dist/core/actions.d.ts +71 -17
- package/dist/core/agentic-process-forms.d.ts +51 -0
- package/dist/core/agentic-process-forms.js +49 -0
- package/dist/core/binding-messages.d.ts +5 -0
- package/dist/core/cli-library.d.ts +16 -7
- package/dist/core/cli-library.js +53 -19
- package/dist/core/comment-text.d.ts +29 -0
- package/dist/core/comment-text.js +40 -0
- package/dist/core/connector-checks.d.ts +18 -2
- package/dist/core/connector-checks.js +15 -5
- package/dist/core/contribution-bindings.d.ts +73 -0
- package/dist/core/contribution-bindings.js +105 -0
- package/dist/core/error-port.d.ts +25 -0
- package/dist/core/error-port.js +36 -0
- package/dist/core/expr-check.js +7 -2
- package/dist/core/expr.d.ts +11 -5
- package/dist/core/expr.js +9 -3
- package/dist/core/json-file.d.ts +1 -0
- package/dist/core/json-file.js +12 -0
- package/dist/core/library.d.ts +19 -0
- package/dist/core/library.js +22 -6
- package/dist/core/script-return.js +4 -1
- package/dist/core/typed-raw-node.js +1 -0
- package/dist/core/typescript-module.d.ts +2 -0
- package/dist/core/typescript-module.js +18 -0
- package/dist/core-definitions.json +210 -0
- package/dist/decompile-cli.js +2 -1
- package/dist/decompile.js +64 -24
- package/dist/emit.js +4 -3
- package/dist/flow-sdk.d.ts +18 -12
- package/dist/flow-sdk.js +19 -8
- package/dist/flow-step-tree.d.ts +2 -3
- package/dist/flow-step-tree.js +2 -3
- package/dist/load.js +1 -1
- package/dist/merge-cli.js +3 -2
- package/dist/merge.d.ts +2 -1
- package/dist/merge.js +0 -0
- package/dist/path-within.d.ts +33 -0
- package/dist/path-within.js +41 -0
- package/dist/registry/archive/extract.js +2 -5
- package/dist/registry/curated-overlay.js +3 -2
- package/dist/registry/generators/descriptors.js +20 -18
- package/dist/registry/generators/triggers.js +4 -4
- package/dist/registry/prepare/messages.js +2 -1
- package/dist/registry/prepare/source-labels.js +60 -1
- package/dist/registry/prepare-cli.js +14 -4
- package/dist/registry/pull-cli.js +3 -2
- package/dist/registry/uip/client.d.ts +28 -3
- package/dist/registry/uip/client.js +95 -34
- package/dist/registry/uip/host.d.ts +41 -0
- package/dist/registry/uip/host.js +312 -0
- package/dist/serialize.d.ts +7 -0
- package/dist/serialize.js +414 -277
- package/dist/workdir.js +1 -1
- package/package.json +3 -3
package/dist/flow-sdk.d.ts
CHANGED
|
@@ -373,7 +373,8 @@ export interface NodeOptions {
|
|
|
373
373
|
updates?: Record<string, Expr | unknown>;
|
|
374
374
|
/**
|
|
375
375
|
* What the canvas shows on this node (`display.label`). Defaults to the step
|
|
376
|
-
* name
|
|
376
|
+
* name; on a trigger (`.trigger()`, `.entryPoint()`) to the trigger
|
|
377
|
+
* definition's label, e.g. `Manual trigger`.
|
|
377
378
|
*
|
|
378
379
|
* @remarks
|
|
379
380
|
* Purely cosmetic, and deliberately separate from the step name: the NAME is
|
|
@@ -386,8 +387,10 @@ export interface NodeOptions {
|
|
|
386
387
|
* .step('fetchInv', http({ url, managed: true }), { label: 'Fetch invoice' })
|
|
387
388
|
* ```
|
|
388
389
|
*
|
|
389
|
-
* `.branch()`, `.switch()`, `.loop()`, `.doWhile()`, `.parallel()
|
|
390
|
-
* `.
|
|
390
|
+
* `.branch()`, `.switch()`, `.loop()`, `.doWhile()`, `.parallel()`,
|
|
391
|
+
* `.trigger()` and `.entryPoint()` take it here too, in their options
|
|
392
|
+
* argument. Only `.terminate()` also has a positional label
|
|
393
|
+
* (`.terminate('stop', 'Stop here')`); when both are given, this option wins.
|
|
391
394
|
*/
|
|
392
395
|
label?: string;
|
|
393
396
|
}
|
|
@@ -663,7 +666,7 @@ export interface BuiltEntryPoint {
|
|
|
663
666
|
id: string;
|
|
664
667
|
/** What fires this root: any built-in trigger factory, or a custom trigger. */
|
|
665
668
|
trigger: TriggerSpec;
|
|
666
|
-
/** Node options for the trigger node — exact `version` selection
|
|
669
|
+
/** Node options for the trigger node — exact `version` selection and canvas `label`. */
|
|
667
670
|
options?: NodeOptions;
|
|
668
671
|
/** Inputs scoped to this root (globals bound to it via `triggerNodeId`). */
|
|
669
672
|
inputs: VarDecl[];
|
|
@@ -821,7 +824,7 @@ declare class StepList {
|
|
|
821
824
|
* even a legal call: `out(step, path?)` takes two arguments.
|
|
822
825
|
*
|
|
823
826
|
* **Three ways the handler can end**: `.return(...)` answers the flow,
|
|
824
|
-
* `.terminate(...)` stops the run, and `h.
|
|
827
|
+
* `.terminate(...)` stops the run, and `h.stepToRef('<step>')` compensates and
|
|
825
828
|
* hands control back to a step the success path also reaches — the shape most
|
|
826
829
|
* deployed flows use. A handler that just runs out of steps still gets its own
|
|
827
830
|
* End: rejoining is something the author SAYS, never something wiring infers.
|
|
@@ -835,8 +838,9 @@ declare class StepList {
|
|
|
835
838
|
*
|
|
836
839
|
* **A `.loop()` container takes one too.** `core.logic.loop` declares the
|
|
837
840
|
* handle, and a body step's failure routes to it: the container's envelope
|
|
838
|
-
* carries the body's message in `detail` and the failing body step's
|
|
839
|
-
* `
|
|
841
|
+
* carries the body's message in `detail` and the failing body step's label
|
|
842
|
+
* (`display.label`, which defaults to the step name) in `element`, and the
|
|
843
|
+
* instance completes rather than faulting. `.doWhile()` does
|
|
840
844
|
* NOT — it declares an error variable and no error handle — so that one is
|
|
841
845
|
* refused by name, and its failures belong to the body step that can fail.
|
|
842
846
|
*
|
|
@@ -1235,7 +1239,8 @@ declare class FlowBuilder extends StepList {
|
|
|
1235
1239
|
*
|
|
1236
1240
|
* @param spec - What starts the flow, from `scheduled(...)` or `onEvent(...)`.
|
|
1237
1241
|
* @param options - Node options; `version` selects the exact definition
|
|
1238
|
-
* version
|
|
1242
|
+
* version and `label` sets what the canvas shows on the trigger node
|
|
1243
|
+
* (default: the trigger definition's label, e.g. `Scheduled trigger`). See {@link NodeOptions}.
|
|
1239
1244
|
* @returns This builder, so calls chain.
|
|
1240
1245
|
* @defaultValue the manual trigger
|
|
1241
1246
|
*/
|
|
@@ -1281,13 +1286,14 @@ declare class FlowBuilder extends StepList {
|
|
|
1281
1286
|
* @param trigger - What fires this root: `manual()`, `scheduled(...)`,
|
|
1282
1287
|
* `onEvent(...)`, `formTrigger()` (one field per scoped input),
|
|
1283
1288
|
* `conversationTrigger()`, or `voiceTrigger()`.
|
|
1284
|
-
* @param options - `version` (exact trigger definition version)
|
|
1285
|
-
*
|
|
1289
|
+
* @param options - `version` (exact trigger definition version), `label`
|
|
1290
|
+
* (what the canvas shows on the trigger node; default: the trigger
|
|
1291
|
+
* definition's label, e.g. `Manual trigger`), and `inputs` — this root's scoped inputs, declared like
|
|
1292
|
+
* `.input()`.
|
|
1286
1293
|
* @param prefixFn - Optional sub-builder for the root's prefix steps.
|
|
1287
1294
|
* @returns This builder, so calls chain.
|
|
1288
1295
|
*/
|
|
1289
|
-
entryPoint(id: string, trigger: TriggerSpec | FlowTrigger, options?: {
|
|
1290
|
-
version?: string;
|
|
1296
|
+
entryPoint(id: string, trigger: TriggerSpec | FlowTrigger, options?: Pick<NodeOptions, 'version' | 'label'> & {
|
|
1291
1297
|
inputs?: Record<string, TypeDesc | VarSpec>;
|
|
1292
1298
|
}, prefixFn?: (b: StepList) => void): this;
|
|
1293
1299
|
/**
|
package/dist/flow-sdk.js
CHANGED
|
@@ -402,7 +402,7 @@ class StepList {
|
|
|
402
402
|
* even a legal call: `out(step, path?)` takes two arguments.
|
|
403
403
|
*
|
|
404
404
|
* **Three ways the handler can end**: `.return(...)` answers the flow,
|
|
405
|
-
* `.terminate(...)` stops the run, and `h.
|
|
405
|
+
* `.terminate(...)` stops the run, and `h.stepToRef('<step>')` compensates and
|
|
406
406
|
* hands control back to a step the success path also reaches — the shape most
|
|
407
407
|
* deployed flows use. A handler that just runs out of steps still gets its own
|
|
408
408
|
* End: rejoining is something the author SAYS, never something wiring infers.
|
|
@@ -416,8 +416,9 @@ class StepList {
|
|
|
416
416
|
*
|
|
417
417
|
* **A `.loop()` container takes one too.** `core.logic.loop` declares the
|
|
418
418
|
* handle, and a body step's failure routes to it: the container's envelope
|
|
419
|
-
* carries the body's message in `detail` and the failing body step's
|
|
420
|
-
* `
|
|
419
|
+
* carries the body's message in `detail` and the failing body step's label
|
|
420
|
+
* (`display.label`, which defaults to the step name) in `element`, and the
|
|
421
|
+
* instance completes rather than faulting. `.doWhile()` does
|
|
421
422
|
* NOT — it declares an error variable and no error handle — so that one is
|
|
422
423
|
* refused by name, and its failures belong to the body step that can fail.
|
|
423
424
|
*
|
|
@@ -464,7 +465,7 @@ class StepList {
|
|
|
464
465
|
// `@2.4`, gated on `errorHandlingEnabled` like every other error handle), and
|
|
465
466
|
// the platform routes it: a body step throwing inside a measured run left the
|
|
466
467
|
// instance Completed with the container's envelope published — `detail` the
|
|
467
|
-
// body's own message, `element` the
|
|
468
|
+
// body's own message, `element` the label of the body step that failed.
|
|
468
469
|
//
|
|
469
470
|
// `.doWhile()` is NOT: `core.logic.dowhile@1.0` declares an error VARIABLE
|
|
470
471
|
// and no error HANDLE, so an edge would leave a handle the node does not
|
|
@@ -942,7 +943,8 @@ class FlowBuilder extends StepList {
|
|
|
942
943
|
*
|
|
943
944
|
* @param spec - What starts the flow, from `scheduled(...)` or `onEvent(...)`.
|
|
944
945
|
* @param options - Node options; `version` selects the exact definition
|
|
945
|
-
* version
|
|
946
|
+
* version and `label` sets what the canvas shows on the trigger node
|
|
947
|
+
* (default: the trigger definition's label, e.g. `Scheduled trigger`). See {@link NodeOptions}.
|
|
946
948
|
* @returns This builder, so calls chain.
|
|
947
949
|
* @defaultValue the manual trigger
|
|
948
950
|
*/
|
|
@@ -996,8 +998,10 @@ class FlowBuilder extends StepList {
|
|
|
996
998
|
* @param trigger - What fires this root: `manual()`, `scheduled(...)`,
|
|
997
999
|
* `onEvent(...)`, `formTrigger()` (one field per scoped input),
|
|
998
1000
|
* `conversationTrigger()`, or `voiceTrigger()`.
|
|
999
|
-
* @param options - `version` (exact trigger definition version)
|
|
1000
|
-
*
|
|
1001
|
+
* @param options - `version` (exact trigger definition version), `label`
|
|
1002
|
+
* (what the canvas shows on the trigger node; default: the trigger
|
|
1003
|
+
* definition's label, e.g. `Manual trigger`), and `inputs` — this root's scoped inputs, declared like
|
|
1004
|
+
* `.input()`.
|
|
1001
1005
|
* @param prefixFn - Optional sub-builder for the root's prefix steps.
|
|
1002
1006
|
* @returns This builder, so calls chain.
|
|
1003
1007
|
*/
|
|
@@ -1011,7 +1015,14 @@ class FlowBuilder extends StepList {
|
|
|
1011
1015
|
this._entryPoints.push({
|
|
1012
1016
|
id,
|
|
1013
1017
|
trigger: trigger,
|
|
1014
|
-
...(options?.version !== undefined
|
|
1018
|
+
...(options?.version !== undefined || options?.label !== undefined
|
|
1019
|
+
? {
|
|
1020
|
+
options: {
|
|
1021
|
+
...(options.version !== undefined ? { version: options.version } : {}),
|
|
1022
|
+
...(options.label !== undefined ? { label: options.label } : {}),
|
|
1023
|
+
},
|
|
1024
|
+
}
|
|
1025
|
+
: {}),
|
|
1015
1026
|
inputs,
|
|
1016
1027
|
steps: b.steps,
|
|
1017
1028
|
});
|
package/dist/flow-step-tree.d.ts
CHANGED
|
@@ -15,9 +15,8 @@ export declare function stopsAtEnd(steps: readonly Step[]): boolean;
|
|
|
15
15
|
* The step lists nested DIRECTLY inside `step` — the bodies a walk over this
|
|
16
16
|
* flow's own tree must descend into. Exhaustive over `Step['kind']`, so a new
|
|
17
17
|
* container kind fails to compile here instead of being silently skipped by
|
|
18
|
-
* every walker. That is how `check`
|
|
19
|
-
* arms and `.stepToList` / `.onError` bodies
|
|
20
|
-
* whose every path ends in `.return()` (UV-16855).
|
|
18
|
+
* every walker. That is how an earlier `check` return scan came to miss
|
|
19
|
+
* `.stepSwitch` arms and `.stepToList` / `.onError` bodies (UV-16855).
|
|
21
20
|
*
|
|
22
21
|
* A subflow's child flow is deliberately NOT a child here: it is another
|
|
23
22
|
* flow's tree, and its returns say nothing about whether THIS flow ends.
|
package/dist/flow-step-tree.js
CHANGED
|
@@ -15,9 +15,8 @@ export function stopsAtEnd(steps) {
|
|
|
15
15
|
* The step lists nested DIRECTLY inside `step` — the bodies a walk over this
|
|
16
16
|
* flow's own tree must descend into. Exhaustive over `Step['kind']`, so a new
|
|
17
17
|
* container kind fails to compile here instead of being silently skipped by
|
|
18
|
-
* every walker. That is how `check`
|
|
19
|
-
* arms and `.stepToList` / `.onError` bodies
|
|
20
|
-
* whose every path ends in `.return()` (UV-16855).
|
|
18
|
+
* every walker. That is how an earlier `check` return scan came to miss
|
|
19
|
+
* `.stepSwitch` arms and `.stepToList` / `.onError` bodies (UV-16855).
|
|
21
20
|
*
|
|
22
21
|
* A subflow's child flow is deliberately NOT a child here: it is another
|
|
23
22
|
* flow's tree, and its returns say nothing about whether THIS flow ends.
|
package/dist/load.js
CHANGED
|
@@ -13,7 +13,7 @@ export function resolveFlowFile(arg) {
|
|
|
13
13
|
if (!statSync(resolvedCandidate, { throwIfNoEntry: false })?.isFile())
|
|
14
14
|
continue;
|
|
15
15
|
if (candidate !== arg && argStat?.isDirectory()) {
|
|
16
|
-
|
|
16
|
+
process.stderr.write(`flow source: "${resolvedArg}" is a directory; using "${resolvedCandidate}"\n`);
|
|
17
17
|
}
|
|
18
18
|
return resolvedCandidate;
|
|
19
19
|
}
|
package/dist/merge-cli.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* See merge.ts.
|
|
13
13
|
*/
|
|
14
14
|
import { reportResult } from './cli-result.js';
|
|
15
|
-
import {
|
|
15
|
+
import { writeJsonFile } from './core/json-file.js';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
16
17
|
import { basename, resolve } from 'node:path';
|
|
17
18
|
import { mergeFlow } from './merge.js';
|
|
18
19
|
import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
|
|
@@ -37,7 +38,7 @@ export function run(argv) {
|
|
|
37
38
|
warn: (m) => console.error(`flow-merge: warning: ${m}`),
|
|
38
39
|
});
|
|
39
40
|
const out = flagValue(argv, '-o') ?? 'merged.flow';
|
|
40
|
-
|
|
41
|
+
writeJsonFile(out, merged);
|
|
41
42
|
console.log(`flow-merge: wrote ${basename(out)} (${(merged.nodes ?? []).length} nodes, ${(merged.edges ?? []).length} edges)`);
|
|
42
43
|
reportResult({ path: resolve(out), nodes: (merged.nodes ?? []).length, edges: (merged.edges ?? []).length });
|
|
43
44
|
return 0;
|
package/dist/merge.d.ts
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* as decompile was faithful for every node (the common case).
|
|
26
26
|
*
|
|
27
27
|
* Layout, `metadata`, `solutionId`, `projectId`, `runtime` and `layout` are
|
|
28
|
-
* preserved from the original; `
|
|
28
|
+
* preserved from the original; `subflows` and edges (with their ids) come from
|
|
29
|
+
* `edited`; `definitions` and `bindings` are unioned
|
|
29
30
|
* (original wins on conflict, since the original's are the tenant-resolved
|
|
30
31
|
* authority). Node ids are stable by construction (decompile names every step by
|
|
31
32
|
* its node id), which is what lets every re-attach key on id.
|
package/dist/merge.js
CHANGED
|
Binary file
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* path-within — the one path-containment test for anything that writes files.
|
|
3
|
+
*
|
|
4
|
+
* "Is `child` inside `parent`?" looks like a string-prefix question and is not
|
|
5
|
+
* one. `child.startsWith(parent + '/')` is correct only where the separator is
|
|
6
|
+
* `/`: on Windows `path.resolve` returns `C:\proj\P\x`, the check refuses every
|
|
7
|
+
* path, and compile refused every inline agent's `<source>/agent.json`
|
|
8
|
+
* (flow-builder-sdk#946). A prefix test without a trailing separator has the
|
|
9
|
+
* opposite bug: it accepts the sibling `C:\proj\P2\x` as inside `C:\proj\P`.
|
|
10
|
+
*
|
|
11
|
+
* So the test is `relative(parent, child)` instead, which gets both right on
|
|
12
|
+
* every platform, and Windows' case-insensitive drive letters and
|
|
13
|
+
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
|
+
*
|
|
15
|
+
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
|
+
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
|
+
*/
|
|
18
|
+
import * as nodePath from 'node:path';
|
|
19
|
+
/** The slice of `node:path` the test needs; `path.win32` and `path.posix` both fit. */
|
|
20
|
+
export type PathApi = Pick<typeof nodePath, 'relative' | 'isAbsolute' | 'sep'>;
|
|
21
|
+
/**
|
|
22
|
+
* `child` is `parent` itself or somewhere beneath it.
|
|
23
|
+
*
|
|
24
|
+
* Both paths should be absolute (from `path.resolve`); relative ones are
|
|
25
|
+
* resolved against the working directory by `relative`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* isWithin('/proj/P', '/proj/P/abc/agent.json') // true
|
|
29
|
+
* isWithin('/proj/P', '/proj/P2/agent.json') // false — sibling, not child
|
|
30
|
+
* isWithin('C:\\proj\\P', 'C:\\proj\\P\\abc\\agent.json', path.win32) // true
|
|
31
|
+
* isWithin('C:\\proj\\P', 'D:\\agent.json', path.win32) // false — other drive
|
|
32
|
+
*/
|
|
33
|
+
export declare function isWithin(parent: string, child: string, path?: PathApi): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* path-within — the one path-containment test for anything that writes files.
|
|
3
|
+
*
|
|
4
|
+
* "Is `child` inside `parent`?" looks like a string-prefix question and is not
|
|
5
|
+
* one. `child.startsWith(parent + '/')` is correct only where the separator is
|
|
6
|
+
* `/`: on Windows `path.resolve` returns `C:\proj\P\x`, the check refuses every
|
|
7
|
+
* path, and compile refused every inline agent's `<source>/agent.json`
|
|
8
|
+
* (flow-builder-sdk#946). A prefix test without a trailing separator has the
|
|
9
|
+
* opposite bug: it accepts the sibling `C:\proj\P2\x` as inside `C:\proj\P`.
|
|
10
|
+
*
|
|
11
|
+
* So the test is `relative(parent, child)` instead, which gets both right on
|
|
12
|
+
* every platform, and Windows' case-insensitive drive letters and
|
|
13
|
+
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
|
+
*
|
|
15
|
+
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
|
+
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
|
+
*/
|
|
18
|
+
import * as nodePath from 'node:path';
|
|
19
|
+
/**
|
|
20
|
+
* `child` is `parent` itself or somewhere beneath it.
|
|
21
|
+
*
|
|
22
|
+
* Both paths should be absolute (from `path.resolve`); relative ones are
|
|
23
|
+
* resolved against the working directory by `relative`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* isWithin('/proj/P', '/proj/P/abc/agent.json') // true
|
|
27
|
+
* isWithin('/proj/P', '/proj/P2/agent.json') // false — sibling, not child
|
|
28
|
+
* isWithin('C:\\proj\\P', 'C:\\proj\\P\\abc\\agent.json', path.win32) // true
|
|
29
|
+
* isWithin('C:\\proj\\P', 'D:\\agent.json', path.win32) // false — other drive
|
|
30
|
+
*/
|
|
31
|
+
export function isWithin(parent, child, path = nodePath) {
|
|
32
|
+
const rel = path.relative(parent, child);
|
|
33
|
+
if (rel === '')
|
|
34
|
+
return true;
|
|
35
|
+
// `..` alone or as the first segment leaves `parent`; a segment that merely
|
|
36
|
+
// starts with dots (`..cache`) does not.
|
|
37
|
+
if (rel === '..' || rel.startsWith(`..${path.sep}`))
|
|
38
|
+
return false;
|
|
39
|
+
// Windows: a child on another drive has no relative spelling.
|
|
40
|
+
return !path.isAbsolute(rel);
|
|
41
|
+
}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* Python printed to stderr; the caller prints it and exits 1.
|
|
18
18
|
*/
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
|
-
import { join, resolve
|
|
20
|
+
import { join, resolve } from "node:path";
|
|
21
|
+
import { isWithin } from "../../path-within.js";
|
|
21
22
|
import { isFile, writeBytes, ensureDir } from "../files.js";
|
|
22
23
|
import { UnsafeArchive, isSymlinkEntry, safeRelativePath } from "./entry-policy.js";
|
|
23
24
|
import { NotAZip, UnsupportedArchive, entryData, readZipEntries } from "./zip-reader.js";
|
|
@@ -40,10 +41,6 @@ export class ExtractLibraryError extends Error {
|
|
|
40
41
|
this.name = "ExtractLibraryError";
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
/** `child` is `parent` or lives beneath it — both already `path.resolve`d. */
|
|
44
|
-
function isWithin(parent, child) {
|
|
45
|
-
return child === parent || child.startsWith(parent.endsWith(sep) ? parent : parent + sep);
|
|
46
|
-
}
|
|
47
44
|
export function extractArchive(options) {
|
|
48
45
|
const { archivePath, destination } = options;
|
|
49
46
|
const root = options.root ?? DEFAULT_ROOT;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { cp, mkdir, readdir, readFile
|
|
2
|
+
import { cp, mkdir, readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { sortedStrings } from "./compat/compare.js";
|
|
6
|
+
import { writeJsonFile } from "../core/json-file.js";
|
|
6
7
|
import { consoleReporter } from "./reporter.js";
|
|
7
8
|
/** The corrections and definitions that the raw registry dump does not carry. */
|
|
8
9
|
export function bundledOverlayPath() {
|
|
@@ -129,7 +130,7 @@ reporter = consoleReporter) {
|
|
|
129
130
|
}
|
|
130
131
|
if (additions.length > 0) {
|
|
131
132
|
destination.entries = [...entries, ...additions];
|
|
132
|
-
|
|
133
|
+
writeJsonFile(indexPath, destination);
|
|
133
134
|
}
|
|
134
135
|
return { files: copied, entries: additions.length, skipped: sortedStrings([...skipped]) };
|
|
135
136
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* node type and so would emit the same const twice.
|
|
20
20
|
*/
|
|
21
21
|
import { prepareCommand } from "../../core/cli-spelling.js";
|
|
22
|
+
import { commentText, docComment } from "../../core/comment-text.js";
|
|
22
23
|
import { PACKAGE_NAME } from "../../package-name.js";
|
|
23
24
|
import { pyJsonString, pyJsonStringify } from "../codecs/pyjson.js";
|
|
24
25
|
import { sortedByCodePoint, sortedStrings } from "../compat/compare.js";
|
|
@@ -58,15 +59,16 @@ export function lookupFieldNote(declared) {
|
|
|
58
59
|
export function emitNestedProperty(name, node, indent, namedTypes, allOptional, isInput = false) {
|
|
59
60
|
const lines = [];
|
|
60
61
|
const declared = node.declared ?? {};
|
|
61
|
-
let desc = pyStrip(strOrEmpty(declared.description))
|
|
62
|
+
let desc = pyStrip(strOrEmpty(declared.description));
|
|
62
63
|
if (isInput && pyTruthy(declared.__lookup)) {
|
|
63
64
|
// The identity goes ON the field, not only in the LOOKUP block above the
|
|
64
65
|
// descriptor: "The reporter of the issue" beside `ConnectorLookupValue<string>`
|
|
65
66
|
// read as "any string", and an e-mail went into `fields.reporter.id`.
|
|
66
67
|
desc = pyStrip(`${desc} ${lookupFieldNote(declared)}`);
|
|
67
68
|
}
|
|
69
|
+
// A multi-line description keeps its breaks: `docComment` opens a block for it.
|
|
68
70
|
if (desc)
|
|
69
|
-
lines.push(
|
|
71
|
+
lines.push(...docComment(desc, indent));
|
|
70
72
|
const optional = allOptional || !subtreeRequired(node) ? "?" : "";
|
|
71
73
|
let rendered = namedTypes.typeFor(name, node);
|
|
72
74
|
if (isInput) {
|
|
@@ -100,11 +102,11 @@ export function emitLookupComment(specs, symbol = "<Descriptor>") {
|
|
|
100
102
|
const lines = [];
|
|
101
103
|
for (const [field, spec] of specs) {
|
|
102
104
|
const target = splitOnce(pyStr(pyTruthy(spec.objectName) ? spec.objectName : spec.path), "?")[0];
|
|
103
|
-
lines.push(` * LOOKUP: \`${field}\` -> ${target}.${spec.value}`);
|
|
105
|
+
lines.push(` * LOOKUP: \`${commentText(field)}\` -> ${commentText(target)}.${commentText(spec.value)}`);
|
|
104
106
|
const aliases = spec.aliases;
|
|
105
107
|
const offered = aliases.size > 0 ? [...aliases.values()].map((a) => `${a}()`) : [];
|
|
106
108
|
const byList = spec.by.map((n) => `\`${n}\``).join(", ");
|
|
107
|
-
lines.push(` * by: ${byList}`);
|
|
109
|
+
lines.push(` * by: ${commentText(byList)}`);
|
|
108
110
|
// THE WHOLE CALL, not just the method names: naming `.byEmail` without the
|
|
109
111
|
// `lookup(Descriptor, 'field')` wrapper says helpers exist but not how to
|
|
110
112
|
// reach one.
|
|
@@ -114,16 +116,16 @@ export function emitLookupComment(specs, symbol = "<Descriptor>") {
|
|
|
114
116
|
: spec.by.length > 0
|
|
115
117
|
? `lookup(${symbol}, '${field}').by('${spec.by[0]}', value)`
|
|
116
118
|
: `lookup(${symbol}, '${field}')`;
|
|
117
|
-
lines.push(` * use: ${call}`);
|
|
119
|
+
lines.push(` * use: ${commentText(call)}`);
|
|
118
120
|
if (offered.length > 0) {
|
|
119
|
-
lines.push(` * also: ${[...aliases.values()].map((a) => `.${a}`).join(", ")} · .by(name, value)`);
|
|
121
|
+
lines.push(` * also: ${commentText([...aliases.values()].map((a) => `.${a}`).join(", "))} · .by(name, value)`);
|
|
120
122
|
}
|
|
121
123
|
else {
|
|
122
124
|
lines.push(" * also: .by(name, value) only — aliases collided");
|
|
123
125
|
}
|
|
124
|
-
lines.push(` * strategy: ${STRATEGY_PROSE[spec.strategy]}`);
|
|
126
|
+
lines.push(` * strategy: ${commentText(STRATEGY_PROSE[spec.strategy])}`);
|
|
125
127
|
if (spec.dependsOn && spec.dependsOn.length > 0) {
|
|
126
|
-
lines.push(` * resolve first: ${spec.dependsOn.map((d) => `\`${d}\``).join(", ")}`);
|
|
128
|
+
lines.push(` * resolve first: ${commentText(spec.dependsOn.map((d) => `\`${d}\``).join(", "))}`);
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
return lines;
|
|
@@ -186,16 +188,16 @@ export function emitEvent(entry, inFields, inputTree, outputTree, name, inputTyp
|
|
|
186
188
|
// connector input's are, so the descriptor carries the same specs.
|
|
187
189
|
const specs = specsFromFields(inFields, invariant);
|
|
188
190
|
lines.push("/**");
|
|
189
|
-
lines.push(` * ${pyStr(label)} — ${nodeType}`);
|
|
191
|
+
lines.push(` * ${commentText(pyStr(label))} — ${commentText(nodeType)}`);
|
|
190
192
|
lines.push(" *");
|
|
191
193
|
if (generic) {
|
|
192
194
|
lines.push(" * A GENERIC event: it fires for ONE object of the connection (an entity, a");
|
|
193
|
-
lines.push(` * table, a custom object), so name it — \`onEvent(${name}, { object: '<Object>' })\`.`);
|
|
194
|
-
lines.push(` * \`uip is triggers objects ${pyStr(connectorKey)} <EVENT> --connection-id <id>\` lists them.`);
|
|
195
|
+
lines.push(` * table, a custom object), so name it — \`onEvent(${commentText(name)}, { object: '<Object>' })\`.`);
|
|
196
|
+
lines.push(` * \`uip is triggers objects ${commentText(pyStr(connectorKey))} <EVENT> --connection-id <id>\` lists them.`);
|
|
195
197
|
}
|
|
196
198
|
else {
|
|
197
|
-
lines.push(` * Start a flow on it with \`onEvent(${name}, { where: { … } })\`, or pause for it`);
|
|
198
|
-
lines.push(` * with \`waitForEvent(${name}, { … })\`; \`where\` carries its event parameters.`);
|
|
199
|
+
lines.push(` * Start a flow on it with \`onEvent(${commentText(name)}, { where: { … } })\`, or pause for it`);
|
|
200
|
+
lines.push(` * with \`waitForEvent(${commentText(name)}, { … })\`; \`where\` carries its event parameters.`);
|
|
199
201
|
}
|
|
200
202
|
// The facts the library build stamped from the event's `triggers objects`
|
|
201
203
|
// row. An author holding only the descriptor must learn the requirement here:
|
|
@@ -205,7 +207,7 @@ export function emitEvent(entry, inFields, inputTree, outputTree, name, inputTyp
|
|
|
205
207
|
lines.push(" * Requires a BYOA (bring-your-own-app) connection — on a standard one the");
|
|
206
208
|
lines.push(" * trigger deploys and never fires. Bind it with");
|
|
207
209
|
// Spelled for whoever ran the generator, like every `check` remedy.
|
|
208
|
-
lines.push(` * \`${prepareCommand(pyStr(connectorKey), eventId, "--connection-id", "<id>")}\`,`);
|
|
210
|
+
lines.push(` * \`${commentText(prepareCommand(pyStr(connectorKey), eventId, "--connection-id", "<id>"))}\`,`);
|
|
209
211
|
lines.push(" * which refuses a standard connection and records the check `compile` requires;");
|
|
210
212
|
lines.push(" * `uip is connections list --byoa --all-folders` lists the BYOA connections.");
|
|
211
213
|
}
|
|
@@ -253,7 +255,7 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
253
255
|
const schemaAction = operation.schemaAction ?? {};
|
|
254
256
|
const specs = lookupSpecs(entry, v1def, invariant);
|
|
255
257
|
const lookupComment = specs.size > 0 ? emitLookupComment(specs, name) : [];
|
|
256
|
-
const header = ` * ${pyStr(label)} — ${nodeType}`;
|
|
258
|
+
const header = ` * ${commentText(pyStr(label))} — ${commentText(nodeType)}`;
|
|
257
259
|
if (pyTruthy(operation.objectFromDiscovery)) {
|
|
258
260
|
lines.push("/**");
|
|
259
261
|
lines.push(header);
|
|
@@ -272,8 +274,8 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
272
274
|
lines.push("/**");
|
|
273
275
|
lines.push(header);
|
|
274
276
|
lines.push(" *");
|
|
275
|
-
lines.push(` * \`Inputs\` carries only the fields that SELECT the schema (${parents}).`);
|
|
276
|
-
lines.push(` * The real set comes from the \`${pyStr(schemaAction.name)}\` design-time`);
|
|
277
|
+
lines.push(` * \`Inputs\` carries only the fields that SELECT the schema (${commentText(parents)}).`);
|
|
278
|
+
lines.push(` * The real set comes from the \`${commentText(pyStr(schemaAction.name))}\` design-time`);
|
|
277
279
|
lines.push(" * action — describe with those values supplied to see it.");
|
|
278
280
|
if (lookupComment.length > 0) {
|
|
279
281
|
lines.push(" *");
|
|
@@ -289,7 +291,7 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
289
291
|
lines.push(" */");
|
|
290
292
|
}
|
|
291
293
|
else {
|
|
292
|
-
lines.push(`/** ${pyStr(label)} — ${nodeType} */`);
|
|
294
|
+
lines.push(`/** ${commentText(pyStr(label))} — ${commentText(nodeType)} */`);
|
|
293
295
|
}
|
|
294
296
|
lines.push(`export const ${name} = descriptor<${name}Inputs, ${name}Outputs>()({`);
|
|
295
297
|
lines.push(` nodeType: ${pyJsonString(nodeType)},`);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* with it, and `tests/registry-triggers-gen.test.ts` now pins the text.
|
|
12
12
|
*/
|
|
13
13
|
import { PACKAGE_NAME } from "../../package-name.js";
|
|
14
|
+
import { docComment } from "../../core/comment-text.js";
|
|
14
15
|
import { pyJsonString, pyJsonStringify } from "../codecs/pyjson.js";
|
|
15
16
|
import { sortedByCodePoint, sortedStrings } from "../compat/compare.js";
|
|
16
17
|
import { afterLast, pyRstrip, pyStrip, upperFirst } from "../compat/pystr.js";
|
|
@@ -32,11 +33,10 @@ function emitTrigger(row, seen) {
|
|
|
32
33
|
seen.add(name);
|
|
33
34
|
const version = "Version" in row ? row.Version : "1.0.0";
|
|
34
35
|
const label = row.DisplayName || eventId;
|
|
35
|
-
const description = pyStrip(String(row.Description || ""))
|
|
36
|
-
|
|
37
|
-
doc = doc.replaceAll("*/", "* /"); // never let a description close the doc comment early
|
|
36
|
+
const description = pyStrip(String(row.Description || ""));
|
|
37
|
+
const doc = description ? `${label} — ${description}` : label;
|
|
38
38
|
return [
|
|
39
|
-
|
|
39
|
+
...docComment(doc),
|
|
40
40
|
`export const ${name} = triggerDescriptor()({`,
|
|
41
41
|
` connector: ${pyJsonString(row.connectorKey)},`,
|
|
42
42
|
` event: ${pyJsonString(eventId)},`,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* `core/cli-spelling.ts`) without this module reading the environment.
|
|
22
22
|
*/
|
|
23
23
|
import { Refusal, messageOf } from "../errors.js";
|
|
24
|
+
import { commentText } from "../../core/comment-text.js";
|
|
24
25
|
import { plausibleMatches } from "./actions.js";
|
|
25
26
|
import { pyGet, pyOr, pyRepr, pyStrAny, pyTruthy } from "./support.js";
|
|
26
27
|
export const PREFIX = "prepare-connector: ";
|
|
@@ -637,7 +638,7 @@ export function descriptorUsageUnparsedLines(sourcePosix, descriptorName) {
|
|
|
637
638
|
export function descriptorUsageLines(descriptorName, importPath, requiredFields, boundConnection, boundFolder) {
|
|
638
639
|
let inputs = "{}";
|
|
639
640
|
if (requiredFields.length > 0) {
|
|
640
|
-
const placeholders = requiredFields.map(([name, type]) => `${name}: /* ${type}, required */`).join(", ");
|
|
641
|
+
const placeholders = requiredFields.map(([name, type]) => `${name}: /* ${commentText(type)}, required */`).join(", ");
|
|
641
642
|
inputs = `{ ${placeholders} }`;
|
|
642
643
|
}
|
|
643
644
|
return [
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
* import), so a source that a first `prepare` already repointed is still read;
|
|
23
23
|
* - `onEvent(...)` / `waitForEvent(...)` — a subscription object whose
|
|
24
24
|
* `connector` is `'<key>'`, or a descriptor imported as above with its options.
|
|
25
|
+
* - `inlineAgent({ tools })` — each `{ kind: 'connector', connector: '<key>',
|
|
26
|
+
* connection, folder }` tool, a single tool or an array (flow-builder-sdk#903).
|
|
27
|
+
* A tool that is not a same-file object may be a connector tool, so it is
|
|
28
|
+
* unplaceable like any other call; a tool of another `kind` binds nothing.
|
|
25
29
|
*
|
|
26
30
|
* `opts` and the labels in it may be literals or same-file `const`s, through
|
|
27
31
|
* `as const`, `satisfies`, parentheses, spreads of such consts, and property
|
|
@@ -47,7 +51,7 @@ import { relativePosix } from "../compat/paths.js";
|
|
|
47
51
|
import { canonicalPath, flowSources, isRelativeSpecifier } from "./source-import.js";
|
|
48
52
|
import { inWorkDir } from "../../workdir.js";
|
|
49
53
|
const CALLEES = new Set([
|
|
50
|
-
"connector", "onEvent", "waitForEvent", "eventTrigger", "dataService",
|
|
54
|
+
"connector", "onEvent", "waitForEvent", "eventTrigger", "dataService", "inlineAgent",
|
|
51
55
|
]);
|
|
52
56
|
/**
|
|
53
57
|
* Data Fabric's connector key, spelled here rather than imported from
|
|
@@ -313,6 +317,58 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
|
|
|
313
317
|
result.unreadable.connection.push(line);
|
|
314
318
|
result.unreadable.folder.push(line);
|
|
315
319
|
};
|
|
320
|
+
/**
|
|
321
|
+
* An inline agent's `tools`: one tool or an array of them. A connector tool
|
|
322
|
+
* carries its labels on the tool object itself, beside its `connector` key.
|
|
323
|
+
*/
|
|
324
|
+
const readAgentTools = (spec, at) => {
|
|
325
|
+
const obj = constObject(spec);
|
|
326
|
+
if (!obj) {
|
|
327
|
+
unplaceable(at, `takes \`${snippet(unwrap(spec))}\`, which is not a same-file object, so its tools`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const tools = prop(obj, "tools");
|
|
331
|
+
if (tools.kind === "absent")
|
|
332
|
+
return;
|
|
333
|
+
if (tools.kind === "unknown") {
|
|
334
|
+
unplaceable(at, `takes its tools from ${tools.why}`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const list = constArray(tools.expr);
|
|
338
|
+
if (!list && !constObject(tools.expr)) {
|
|
339
|
+
unplaceable(at, `takes its tools as \`${snippet(unwrap(tools.expr))}\``);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const elements = list ? expandArgs(list.elements) : [tools.expr];
|
|
343
|
+
if (!Array.isArray(elements)) {
|
|
344
|
+
unplaceable(at, `${elements.why} in its tools`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
for (const el of elements) {
|
|
348
|
+
const toolAt = `${where(el, "inlineAgent")} tool`;
|
|
349
|
+
const tool = constObject(el);
|
|
350
|
+
if (!tool) {
|
|
351
|
+
unplaceable(toolAt, `is \`${snippet(unwrap(el))}\`, which is not a same-file object`);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const kindProp = prop(tool, "kind");
|
|
355
|
+
if (kindProp.kind === "absent")
|
|
356
|
+
continue;
|
|
357
|
+
const kind = kindProp.kind === "found" ? constString(kindProp.expr) : undefined;
|
|
358
|
+
if (kind === undefined) {
|
|
359
|
+
unplaceable(toolAt, "computes its `kind`");
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (kind !== "connector")
|
|
363
|
+
continue;
|
|
364
|
+
const keyProp = prop(tool, "connector");
|
|
365
|
+
const key = keyProp.kind === "found" ? constString(keyProp.expr) : undefined;
|
|
366
|
+
if (key)
|
|
367
|
+
record(key, el, toolAt);
|
|
368
|
+
else
|
|
369
|
+
unplaceable(toolAt, keyProp.kind === "absent" ? "has no `connector`" : "computes its `connector`");
|
|
370
|
+
}
|
|
371
|
+
};
|
|
316
372
|
const visitCalls = (node) => {
|
|
317
373
|
if (ts.isCallExpression(node)) {
|
|
318
374
|
const callee = ts.isIdentifier(node.expression)
|
|
@@ -343,6 +399,9 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
|
|
|
343
399
|
record(key, args[callee === "connector" ? 4 : 3], at);
|
|
344
400
|
}
|
|
345
401
|
}
|
|
402
|
+
else if (callee === "inlineAgent") {
|
|
403
|
+
readAgentTools(args[0], at);
|
|
404
|
+
}
|
|
346
405
|
else if (callee === "connector") {
|
|
347
406
|
const [first] = args;
|
|
348
407
|
const literalKey = constString(first);
|
|
@@ -58,10 +58,20 @@ export function pipelineOptions(options) {
|
|
|
58
58
|
* `uip` client and reporter injected so tests replay recordings.
|
|
59
59
|
*/
|
|
60
60
|
export async function prepareConnector(options) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// One `uip mcp serve` for the ten-or-so calls a prepare makes, instead of a
|
|
62
|
+
// process start per call (flow-builder-sdk#965). Closed when the pipeline
|
|
63
|
+
// is done, whichever way it ends; a host that could not be used says so
|
|
64
|
+
// once, as a warning, and the calls spawn as they did before.
|
|
65
|
+
const client = defaultUipClient({ host: { onFallback: (reason) => consoleReporter.warn(` WARN: ${reason}`) } });
|
|
66
|
+
try {
|
|
67
|
+
return await runPipeline(pipelineOptions(options), {
|
|
68
|
+
client,
|
|
69
|
+
reporter: consoleReporter,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
client.close?.();
|
|
74
|
+
}
|
|
65
75
|
}
|
|
66
76
|
function value(args, name) {
|
|
67
77
|
const index = args.indexOf(name);
|
|
@@ -2,6 +2,7 @@ import { reportResult } from '../cli-result.js';
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { mkdir, readFile, rename, rm, stat, symlink, writeFile, } from "node:fs/promises";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
+
import { writeJsonFile } from "../core/json-file.js";
|
|
5
6
|
import { CURRENT_LINK, LIBRARY_JSON_DIR, LIBRARY_MARKDOWN_DIR, cacheEntry, libraryCacheRoot, referencedConnectorKeys, } from "../core/library-cache.js";
|
|
6
7
|
import { extractLibrary } from "./extractor.js";
|
|
7
8
|
import { materializeRegistry } from "./materializer.js";
|
|
@@ -210,14 +211,14 @@ async function isCurrentLibrary(libraryPath, sha256) {
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
async function writeSource(libraryPath, archive, sha256, archiveSha256, overlaySha256) {
|
|
213
|
-
|
|
214
|
+
writeJsonFile(join(libraryPath, SOURCE_FILE), {
|
|
214
215
|
sha256,
|
|
215
216
|
archiveSha256,
|
|
216
217
|
overlaySha256,
|
|
217
218
|
url: archive.url ?? null,
|
|
218
219
|
archive: archive.checksum ? undefined : archive.path,
|
|
219
220
|
pulledAt: new Date().toISOString(),
|
|
220
|
-
}
|
|
221
|
+
});
|
|
221
222
|
}
|
|
222
223
|
/**
|
|
223
224
|
* Point `current` at an entry, replacing whatever it named.
|