@junoflow/cli 0.10.0 → 0.12.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 +31 -0
- package/package.json +2 -2
- package/src/api.js +15 -7
- package/src/commands/app-check.js +8 -1
- package/src/commands/app-deploy.js +8 -1
- package/src/commands/check.js +8 -1
- package/src/commands/deploy.js +8 -1
- package/src/commands/flow-check.js +8 -2
- package/src/commands/flow-deploy.js +12 -6
- package/src/diagnostics.js +10 -0
- package/src/working-copy.js +60 -0
- package/tsconfig.base.json +1 -0
- package/verbs.json +14 -0
package/README.md
CHANGED
|
@@ -185,6 +185,14 @@ The group's working-copy commands (`integration init`, `gen-types`,
|
|
|
185
185
|
| `juno credential list` | `list_credentials` | — | read |
|
|
186
186
|
| `juno credential set <id>` | none | credential id | act |
|
|
187
187
|
|
|
188
|
+
**`person`**
|
|
189
|
+
|
|
190
|
+
| Command | Tool | Positional | Effect |
|
|
191
|
+
| ------------------- | -------------- | ---------- | ------ |
|
|
192
|
+
| `juno person list` | `list_people` | — | read |
|
|
193
|
+
| `juno person get` | `get_person` | `id` | read |
|
|
194
|
+
| `juno person write` | `write_person` | `id` | change |
|
|
195
|
+
|
|
188
196
|
**`ha`** (Home Assistant)
|
|
189
197
|
|
|
190
198
|
| Command | Tool | Positional | Effect |
|
|
@@ -518,6 +526,29 @@ keeps them in `package.json` alone, so there `pull` leaves `dependencies` as it
|
|
|
518
526
|
is. It refuses to overwrite uncommitted changes in a git working tree unless you
|
|
519
527
|
pass `--force`.
|
|
520
528
|
|
|
529
|
+
### The source, and the warning
|
|
530
|
+
|
|
531
|
+
Every kind's `check` and `deploy` send where the working copy sits in git: the
|
|
532
|
+
`origin` remote (the first remote without one; any user, password, query or
|
|
533
|
+
fragment stripped), the `HEAD` sha, and `dirty` when `git status` reports a
|
|
534
|
+
change or an untracked file under the working copy's directory. Outside git, or
|
|
535
|
+
before the first commit, they send nothing and deploy as usual. Juno records it
|
|
536
|
+
on the version, which is from then on **remote**: its source of truth is this
|
|
537
|
+
working copy, not Juno.
|
|
538
|
+
|
|
539
|
+
When a deploy moves the artifact's source, or replaces a version written in Juno
|
|
540
|
+
since your last pull, the instance answers one sentence and the CLI prints it on
|
|
541
|
+
stderr:
|
|
542
|
+
|
|
543
|
+
```text
|
|
544
|
+
warning: flow "porch-light" was local; v8 is remote from now on — change it in the working copy and deploy.
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
It is never a failure: the deploy has happened, and the exit code is what it
|
|
548
|
+
would have been. `check` prints the warning the deploy would. With `--json` the
|
|
549
|
+
sentence is the body's `source_warning`. See
|
|
550
|
+
[Where the Source Lives](https://boivie.github.io/juno/assistant/source/).
|
|
551
|
+
|
|
521
552
|
## Flows as files
|
|
522
553
|
|
|
523
554
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junoflow/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Develop Juno flows, integrations and apps, and operate a Juno instance from the command line",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@junoflow/eslint-plugin": "0.
|
|
43
|
+
"@junoflow/eslint-plugin": "0.12.0",
|
|
44
44
|
"@typescript-eslint/parser": "^8.0.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
package/src/api.js
CHANGED
|
@@ -87,9 +87,10 @@ export class JunoClient {
|
|
|
87
87
|
*
|
|
88
88
|
* It does not go through #workingCopyJSON, whose 422-without-diagnostics
|
|
89
89
|
* wording differs from what this route has always printed; only the 409 is
|
|
90
|
-
* shared.
|
|
90
|
+
* shared. `origin` (gitOrigin) is sent when given, dry runs included: they
|
|
91
|
+
* stamp nothing, but their source_warning says what a deploy would.
|
|
91
92
|
*/
|
|
92
|
-
async ingest(project, { dryRun = false, id, baseVersion } = {}) {
|
|
93
|
+
async ingest(project, { dryRun = false, id, baseVersion, origin } = {}) {
|
|
93
94
|
const path = `/api/integrations/ingest${dryRun ? '?dry_run=true' : ''}`;
|
|
94
95
|
const res = await this.#request('POST', path, {
|
|
95
96
|
manifest: project.manifest,
|
|
@@ -98,6 +99,7 @@ export class JunoClient {
|
|
|
98
99
|
lockfile: project.lockfile ?? undefined,
|
|
99
100
|
id: id ?? undefined,
|
|
100
101
|
base_version: baseVersion ?? undefined,
|
|
102
|
+
origin: origin ?? undefined,
|
|
101
103
|
});
|
|
102
104
|
if (res.status === 409) throw new ConflictError(problemDetail(res));
|
|
103
105
|
return this.#answer('POST', path, res);
|
|
@@ -187,16 +189,20 @@ export class JunoClient {
|
|
|
187
189
|
* POST /api/flows/ingest — verify a flow working copy and, unless dryRun,
|
|
188
190
|
* commit and activate it. `files` is the working copy's src/ tree, path →
|
|
189
191
|
* content. `baseVersion` undefined sends none (a new flow,
|
|
190
|
-
* or --force).
|
|
191
|
-
* DiagnosticsError.
|
|
192
|
+
* or --force). `origin` (gitOrigin) is sent when given. A 409 is a
|
|
193
|
+
* ConflictError; a 422 with diagnostics is a DiagnosticsError.
|
|
192
194
|
*/
|
|
193
|
-
async ingestFlow(
|
|
195
|
+
async ingestFlow(
|
|
196
|
+
{ flowId, manifest, files, baseVersion },
|
|
197
|
+
{ dryRun, origin } = {}
|
|
198
|
+
) {
|
|
194
199
|
const query = dryRun ? '?dry_run=true' : '';
|
|
195
200
|
return this.#workingCopyJSON(`/api/flows/ingest${query}`, {
|
|
196
201
|
flow_id: flowId,
|
|
197
202
|
manifest,
|
|
198
203
|
files,
|
|
199
204
|
base_version: baseVersion ?? undefined,
|
|
205
|
+
origin: origin ?? undefined,
|
|
200
206
|
});
|
|
201
207
|
}
|
|
202
208
|
|
|
@@ -227,11 +233,12 @@ export class JunoClient {
|
|
|
227
233
|
* `baseVersion` undefined sends none (a copy that records no base, or
|
|
228
234
|
* --force). When activating, a 409 is a ConflictError: the app's active
|
|
229
235
|
* version is not the copy's base. A draft is never refused; the answer's
|
|
230
|
-
* `stale_base` carries the sentence instead.
|
|
236
|
+
* `stale_base` carries the sentence instead. `origin` (gitOrigin) is sent
|
|
237
|
+
* when given.
|
|
231
238
|
*/
|
|
232
239
|
async ingestApp(
|
|
233
240
|
{ manifest, files, lockfile },
|
|
234
|
-
{ activate = true, baseVersion } = {}
|
|
241
|
+
{ activate = true, baseVersion, origin } = {}
|
|
235
242
|
) {
|
|
236
243
|
return this.#workingCopyJSON('/api/apps/ingest', {
|
|
237
244
|
manifest,
|
|
@@ -239,6 +246,7 @@ export class JunoClient {
|
|
|
239
246
|
lockfile: lockfile ?? undefined,
|
|
240
247
|
activate,
|
|
241
248
|
base_version: baseVersion ?? undefined,
|
|
249
|
+
origin: origin ?? undefined,
|
|
242
250
|
});
|
|
243
251
|
}
|
|
244
252
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { connect } from '../api.js';
|
|
2
2
|
import { readAppProject } from '../app-project.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
printDiagnosticsJSON,
|
|
5
|
+
printJSON,
|
|
6
|
+
printSourceWarning,
|
|
7
|
+
} from '../diagnostics.js';
|
|
4
8
|
import { DiagnosticsError } from '../errors.js';
|
|
5
9
|
import { EXIT_CONFLICT } from '../exit.js';
|
|
6
10
|
import { runLocalChecks } from './check.js';
|
|
7
11
|
import { warnOnDependencyDrift } from './flow-check.js';
|
|
12
|
+
import { gitOrigin } from '../working-copy.js';
|
|
8
13
|
|
|
9
14
|
export const APP_CHECK_USAGE = 'juno app check [--skip-local] [--json]';
|
|
10
15
|
|
|
@@ -35,6 +40,7 @@ export async function appCheck(argv, io) {
|
|
|
35
40
|
result = await client.ingestApp(project, {
|
|
36
41
|
activate: false,
|
|
37
42
|
baseVersion: project.baseVersion,
|
|
43
|
+
origin: await gitOrigin(io.cwd, io.env),
|
|
38
44
|
});
|
|
39
45
|
} catch (err) {
|
|
40
46
|
if (argv.json && err instanceof DiagnosticsError) {
|
|
@@ -47,6 +53,7 @@ export async function appCheck(argv, io) {
|
|
|
47
53
|
printJSON(result, io);
|
|
48
54
|
} else {
|
|
49
55
|
for (const w of result.warnings ?? []) io.warn(`warning: ${w}`);
|
|
56
|
+
printSourceWarning(result, io);
|
|
50
57
|
io.log(
|
|
51
58
|
`${result.id}: verified and stored as draft v${result.version} on ${target}`
|
|
52
59
|
);
|
|
@@ -8,10 +8,15 @@ import {
|
|
|
8
8
|
SRC_DIR,
|
|
9
9
|
updateAppPackage,
|
|
10
10
|
} from '../app-project.js';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
printDiagnosticsJSON,
|
|
13
|
+
printJSON,
|
|
14
|
+
printSourceWarning,
|
|
15
|
+
} from '../diagnostics.js';
|
|
12
16
|
import { DiagnosticsError } from '../errors.js';
|
|
13
17
|
import { deployOnChange } from '../watch.js';
|
|
14
18
|
import {
|
|
19
|
+
gitOrigin,
|
|
15
20
|
MANIFEST,
|
|
16
21
|
manifestText,
|
|
17
22
|
typeFiles,
|
|
@@ -79,6 +84,7 @@ export async function appDeploy(argv, io) {
|
|
|
79
84
|
async function deployProject(client, project, argv, io, target) {
|
|
80
85
|
const result = await client.ingestApp(project, {
|
|
81
86
|
baseVersion: argv.force ? undefined : project.baseVersion,
|
|
87
|
+
origin: await gitOrigin(io.cwd, io.env),
|
|
82
88
|
});
|
|
83
89
|
|
|
84
90
|
const manifestRaw = manifestText(result.manifest);
|
|
@@ -97,6 +103,7 @@ async function deployProject(client, project, argv, io, target) {
|
|
|
97
103
|
printJSON(result, io);
|
|
98
104
|
} else {
|
|
99
105
|
for (const w of result.warnings ?? []) io.warn(`warning: ${w}`);
|
|
106
|
+
printSourceWarning(result, io);
|
|
100
107
|
const files = written.length ? ` (${written.sort().join(', ')})` : '';
|
|
101
108
|
io.log(`deployed ${result.id} v${result.version} to ${target}${files}`);
|
|
102
109
|
if (result.url) io.log(`open: ${result.url}`);
|
package/src/commands/check.js
CHANGED
|
@@ -2,11 +2,16 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { connect } from '../api.js';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
printDiagnosticsJSON,
|
|
7
|
+
printJSON,
|
|
8
|
+
printSourceWarning,
|
|
9
|
+
} from '../diagnostics.js';
|
|
6
10
|
import { DiagnosticsError, UserError } from '../errors.js';
|
|
7
11
|
import { readProject } from '../project.js';
|
|
8
12
|
import { baseFor } from './deploy.js';
|
|
9
13
|
import { verifyTypes } from './verify-types.js';
|
|
14
|
+
import { gitOrigin } from '../working-copy.js';
|
|
10
15
|
|
|
11
16
|
export const CHECK_USAGE =
|
|
12
17
|
'juno integration check [--skip-local] [--id <id>] [--json]';
|
|
@@ -48,6 +53,7 @@ export async function check(argv, io) {
|
|
|
48
53
|
dryRun: true,
|
|
49
54
|
id: argv.id,
|
|
50
55
|
baseVersion: baseFor(project, argv, io),
|
|
56
|
+
origin: await gitOrigin(io.cwd, io.env),
|
|
51
57
|
});
|
|
52
58
|
} catch (err) {
|
|
53
59
|
if (argv.json && err instanceof DiagnosticsError) {
|
|
@@ -60,6 +66,7 @@ export async function check(argv, io) {
|
|
|
60
66
|
printJSON(result, io);
|
|
61
67
|
} else {
|
|
62
68
|
for (const w of result.warnings ?? []) io.warn(`warning: ${w}`);
|
|
69
|
+
printSourceWarning(result, io);
|
|
63
70
|
io.log(`${result.id}: manifest and sources verified against ${target}`);
|
|
64
71
|
}
|
|
65
72
|
|
package/src/commands/deploy.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
|
|
3
3
|
import { connect } from '../api.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
printDiagnosticsJSON,
|
|
6
|
+
printJSON,
|
|
7
|
+
printSourceWarning,
|
|
8
|
+
} from '../diagnostics.js';
|
|
5
9
|
import { DiagnosticsError } from '../errors.js';
|
|
6
10
|
import {
|
|
7
11
|
readManifestRaw,
|
|
@@ -12,6 +16,7 @@ import {
|
|
|
12
16
|
import { stampTypeFiles } from '../stamp.js';
|
|
13
17
|
import { pickTypeFiles } from './gen-types.js';
|
|
14
18
|
import { deployOnChange } from '../watch.js';
|
|
19
|
+
import { gitOrigin } from '../working-copy.js';
|
|
15
20
|
|
|
16
21
|
export const DEPLOY_USAGE =
|
|
17
22
|
'juno integration deploy [--watch] [--force] [--id <id>] [--json]';
|
|
@@ -57,6 +62,7 @@ async function deployOnce(client, argv, io, target) {
|
|
|
57
62
|
const result = await client.ingest(project, {
|
|
58
63
|
id: argv.id,
|
|
59
64
|
baseVersion: argv.force ? undefined : baseFor(project, argv, io),
|
|
65
|
+
origin: await gitOrigin(io.cwd, io.env),
|
|
60
66
|
});
|
|
61
67
|
// Stamped against the manifest this deploy was built from, so the watch loop
|
|
62
68
|
// leaves a tree that `juno integration verify-types` accepts.
|
|
@@ -82,6 +88,7 @@ async function deployOnce(client, argv, io, target) {
|
|
|
82
88
|
return result;
|
|
83
89
|
}
|
|
84
90
|
for (const warning of result.warnings ?? []) io.warn(`warning: ${warning}`);
|
|
91
|
+
printSourceWarning(result, io);
|
|
85
92
|
io.log(
|
|
86
93
|
`deployed ${result.id} v${result.version} to ${target} (${written.sort().join(', ')})`
|
|
87
94
|
);
|
|
@@ -2,7 +2,12 @@ import { connect } from '../api.js';
|
|
|
2
2
|
import { DiagnosticsError } from '../errors.js';
|
|
3
3
|
import { readFlowProject } from '../flow-project.js';
|
|
4
4
|
import { runLocalChecks } from './check.js';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
printDiagnosticsJSON,
|
|
7
|
+
printJSON,
|
|
8
|
+
printSourceWarning,
|
|
9
|
+
} from '../diagnostics.js';
|
|
10
|
+
import { gitOrigin } from '../working-copy.js';
|
|
6
11
|
import { printWarnings } from './flow-deploy.js';
|
|
7
12
|
|
|
8
13
|
export const FLOW_CHECK_USAGE = 'juno flow check [--skip-local] [--json]';
|
|
@@ -28,7 +33,7 @@ export async function flowCheck(argv, io) {
|
|
|
28
33
|
files: project.files,
|
|
29
34
|
baseVersion: project.baseVersion,
|
|
30
35
|
},
|
|
31
|
-
{ dryRun: true }
|
|
36
|
+
{ dryRun: true, origin: await gitOrigin(io.cwd, io.env) }
|
|
32
37
|
);
|
|
33
38
|
} catch (err) {
|
|
34
39
|
if (argv.json && err instanceof DiagnosticsError) {
|
|
@@ -41,6 +46,7 @@ export async function flowCheck(argv, io) {
|
|
|
41
46
|
printJSON(result, io);
|
|
42
47
|
} else {
|
|
43
48
|
printWarnings(result.warnings, io);
|
|
49
|
+
printSourceWarning(result, io);
|
|
44
50
|
io.log(`${result.flow_id}: manifest and src/ verified against ${target}`);
|
|
45
51
|
}
|
|
46
52
|
return failed ? 1 : 0;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
formatDiagnostic,
|
|
7
7
|
printDiagnosticsJSON,
|
|
8
8
|
printJSON,
|
|
9
|
+
printSourceWarning,
|
|
9
10
|
} from '../diagnostics.js';
|
|
10
11
|
import {
|
|
11
12
|
MANIFEST,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
writeFlowFiles,
|
|
18
19
|
} from '../flow-project.js';
|
|
19
20
|
import { deployOnChange } from '../watch.js';
|
|
21
|
+
import { gitOrigin } from '../working-copy.js';
|
|
20
22
|
|
|
21
23
|
export const FLOW_DEPLOY_USAGE =
|
|
22
24
|
'juno flow deploy [--watch] [--force] [--json]';
|
|
@@ -73,12 +75,15 @@ async function deployOnce(client, argv, io, target) {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
async function deployProject(client, project, argv, io, target) {
|
|
76
|
-
const result = await client.ingestFlow(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const result = await client.ingestFlow(
|
|
79
|
+
{
|
|
80
|
+
flowId: project.flowId,
|
|
81
|
+
manifest: project.manifest,
|
|
82
|
+
files: project.files,
|
|
83
|
+
baseVersion: argv.force ? undefined : project.baseVersion,
|
|
84
|
+
},
|
|
85
|
+
{ origin: await gitOrigin(io.cwd, io.env) }
|
|
86
|
+
);
|
|
82
87
|
|
|
83
88
|
const manifestRaw = manifestText(result.manifest);
|
|
84
89
|
const written = await writeFlowFiles(io.cwd, {
|
|
@@ -96,6 +101,7 @@ async function deployProject(client, project, argv, io, target) {
|
|
|
96
101
|
printJSON(result, io);
|
|
97
102
|
} else {
|
|
98
103
|
printWarnings(result.warnings, io);
|
|
104
|
+
printSourceWarning(result, io);
|
|
99
105
|
const files = written.length ? ` (${written.sort().join(', ')})` : '';
|
|
100
106
|
io.log(
|
|
101
107
|
`deployed ${result.flow_id} v${result.version} to ${target}${files}`
|
package/src/diagnostics.js
CHANGED
|
@@ -28,3 +28,13 @@ export function printDiagnosticsJSON(err, io) {
|
|
|
28
28
|
printJSON({ detail: err.message, diagnostics: err.diagnostics }, io);
|
|
29
29
|
return 1;
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A route's `source_warning` — the server's sentence for a deploy that moves
|
|
34
|
+
* an artifact between sources (local, installed, remote) or supersedes a
|
|
35
|
+
* version written in Juno — on stderr as `warning: …`. Never a failure: the
|
|
36
|
+
* write already happened, or a check says what a deploy would do.
|
|
37
|
+
*/
|
|
38
|
+
export function printSourceWarning(result, io) {
|
|
39
|
+
if (result?.source_warning) io.warn(`warning: ${result.source_warning}`);
|
|
40
|
+
}
|
package/src/working-copy.js
CHANGED
|
@@ -249,6 +249,66 @@ export async function dirtyPaths(cwd) {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* gitOrigin is where this working copy sits in git, as the `origin` every
|
|
254
|
+
* ingest route accepts: `{git: {remote, sha, dirty}}` — the origin remote's
|
|
255
|
+
* URL (the first remote when there is no `origin`, none when there is no
|
|
256
|
+
* remote), HEAD's sha, and whether `git status --porcelain` reports anything
|
|
257
|
+
* under this directory, untracked files included. Scoped to the working copy,
|
|
258
|
+
* so changes elsewhere in a monorepo do not make it dirty.
|
|
259
|
+
*
|
|
260
|
+
* Best effort and never fatal: not a repository, no commit yet, no git on
|
|
261
|
+
* PATH or any other failure answers undefined, and the deploy goes ahead
|
|
262
|
+
* without it. The remote loses any credentials here; the server strips them
|
|
263
|
+
* again rather than trusting this.
|
|
264
|
+
*/
|
|
265
|
+
export async function gitOrigin(cwd, env) {
|
|
266
|
+
const git = async (...args) =>
|
|
267
|
+
(await execFileAsync('git', args, { cwd, env })).stdout;
|
|
268
|
+
try {
|
|
269
|
+
const sha = (await git('rev-parse', 'HEAD')).trim();
|
|
270
|
+
const dirty = (await git('status', '--porcelain', '--', '.')).trim() !== '';
|
|
271
|
+
let remote = '';
|
|
272
|
+
try {
|
|
273
|
+
remote = (await git('remote', 'get-url', 'origin')).trim();
|
|
274
|
+
} catch {
|
|
275
|
+
const first = (await git('remote')).split('\n')[0].trim();
|
|
276
|
+
if (first) remote = (await git('remote', 'get-url', first)).trim();
|
|
277
|
+
}
|
|
278
|
+
remote = stripCredentials(remote);
|
|
279
|
+
return {
|
|
280
|
+
git: {
|
|
281
|
+
...(remote ? { remote } : {}),
|
|
282
|
+
sha,
|
|
283
|
+
...(dirty ? { dirty } : {}),
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
} catch {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* stripCredentials drops what can carry a secret from a URL remote — its
|
|
293
|
+
* userinfo, query and fragment (`https://user:token@host/o/r` →
|
|
294
|
+
* `https://host/o/r`). An scp-like remote (`git@host:o/r.git`) names a user
|
|
295
|
+
* and no secret, and is kept. A URL that does not parse is dropped rather
|
|
296
|
+
* than guessed at.
|
|
297
|
+
*/
|
|
298
|
+
export function stripCredentials(remote) {
|
|
299
|
+
if (!remote.includes('://')) return remote;
|
|
300
|
+
try {
|
|
301
|
+
const u = new URL(remote);
|
|
302
|
+
u.username = '';
|
|
303
|
+
u.password = '';
|
|
304
|
+
u.search = '';
|
|
305
|
+
u.hash = '';
|
|
306
|
+
return u.toString();
|
|
307
|
+
} catch {
|
|
308
|
+
return '';
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
252
312
|
export async function readText(cwd, name) {
|
|
253
313
|
try {
|
|
254
314
|
return await readFile(join(cwd, name), 'utf8');
|
package/tsconfig.base.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"//": "The compiler options Juno type-checks with server-side. internal/verify's tsconfig_preset_test.go asserts this file and verify.CompilerOptions() are identical, so `tsc --noEmit` passing here means the deploy will not 422 on a type error. Extend it; do not fork it.",
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"target": "ES2022",
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable", "ESNext.Disposable"],
|
|
5
6
|
"module": "CommonJS",
|
|
6
7
|
"moduleResolution": "node",
|
|
7
8
|
"strict": true,
|
package/verbs.json
CHANGED
|
@@ -222,6 +222,20 @@
|
|
|
222
222
|
{ "command": "webhook write", "tool": "write_webhook", "id_field": "id" },
|
|
223
223
|
{ "command": "webhook fire", "tool": "fire_webhook", "id_field": "id" },
|
|
224
224
|
{ "command": "credential list", "tool": "list_credentials" },
|
|
225
|
+
{ "command": "person list", "tool": "list_people" },
|
|
226
|
+
{ "command": "person get", "tool": "get_person", "id_field": "id" },
|
|
227
|
+
{ "command": "person write", "tool": "write_person", "id_field": "id" },
|
|
228
|
+
{
|
|
229
|
+
"command": "question list",
|
|
230
|
+
"tool": "list_questions",
|
|
231
|
+
"flags": { "closed": { "field": "closed", "type": "string" } }
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"command": "question get",
|
|
235
|
+
"tool": "get_question",
|
|
236
|
+
"id_field": "run_id",
|
|
237
|
+
"flags": { "handle": { "field": "handle_id", "type": "string" } }
|
|
238
|
+
},
|
|
225
239
|
{
|
|
226
240
|
"command": "ha get-entity",
|
|
227
241
|
"tool": "get_entity",
|