@indigoai-us/hq-cli 5.14.1 → 5.16.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/.github/workflows/ci.yml +21 -0
- package/.github/workflows/publish.yml +86 -0
- package/.github/workflows/scripts/smoke-test-pkg.sh +97 -0
- package/dist/commands/cloud-provision.d.ts +25 -0
- package/dist/commands/cloud-provision.js +89 -10
- package/dist/commands/meetings.d.ts +3 -0
- package/dist/commands/meetings.js +374 -0
- package/dist/index.js +5 -2
- package/package.json +3 -2
- package/src/commands/cloud-provision.test.ts +43 -2
- package/src/commands/cloud-provision.ts +135 -8
- package/src/commands/meetings.ts +487 -0
- package/src/index.ts +4 -0
- package/tsconfig.json +12 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [main]
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: 22
|
|
16
|
+
- run: npm ci
|
|
17
|
+
# generate-dsn.mjs only fatals when GITHUB_JOB=publish, so CI builds with
|
|
18
|
+
# an empty BUNDLED_DSN. That's intentional — the DSN is publish-only.
|
|
19
|
+
- run: npm run build --if-present
|
|
20
|
+
- run: npm run typecheck --if-present
|
|
21
|
+
- run: npm test --if-present
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ["v*"]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
publish:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
# Node 24 ships npm 11.x, required for npm's trusted-publisher OIDC
|
|
17
|
+
# token-exchange flow. See hq-cloud's publish.yml for the full
|
|
18
|
+
# rationale (npm 10.x produces masked-404 failures on publish PUT).
|
|
19
|
+
node-version: 24
|
|
20
|
+
registry-url: https://registry.npmjs.org
|
|
21
|
+
|
|
22
|
+
- run: node --version && npm --version
|
|
23
|
+
- run: npm ci
|
|
24
|
+
|
|
25
|
+
- name: Build
|
|
26
|
+
run: npm run build --if-present
|
|
27
|
+
env:
|
|
28
|
+
# generate-dsn.mjs fatals when GITHUB_JOB=publish and this var is
|
|
29
|
+
# missing. Set this as a repo secret to bundle a Sentry DSN into the
|
|
30
|
+
# published binary.
|
|
31
|
+
HQ_CLI_PUBLISH_SENTRY_DSN: ${{ secrets.HQ_CLI_PUBLISH_SENTRY_DSN }}
|
|
32
|
+
|
|
33
|
+
# Inject Sentry debug IDs into compiled JS + map files. Must run after
|
|
34
|
+
# tsc and BEFORE publish so the installed binary carries the same debugId
|
|
35
|
+
# as the artifacts uploaded to Sentry, enabling debugId-based source-map
|
|
36
|
+
# resolution for globally-installed CLI users.
|
|
37
|
+
- name: Inject Sentry source map debug IDs
|
|
38
|
+
run: npx -y @sentry/cli@^2 sourcemaps inject dist/
|
|
39
|
+
|
|
40
|
+
# Pre-publish cross-package smoke test (Bug A guard).
|
|
41
|
+
#
|
|
42
|
+
# @indigoai-us/hq-cli depends on @indigoai-us/hq-cloud via a caret range
|
|
43
|
+
# (^5.1.0). The smoke installs a freshly-packed tarball into an isolated
|
|
44
|
+
# tmpdir — outside this repo — so transitive @indigoai-us/* deps resolve
|
|
45
|
+
# from the npm registry, NOT from a local checkout. Booting `hq --version`
|
|
46
|
+
# then exercises the full module-load graph. If hq-cloud removed or
|
|
47
|
+
# renamed a symbol hq-cli imports, this catches it BEFORE the publish PUT.
|
|
48
|
+
#
|
|
49
|
+
# Bypass: set repo-or-workflow variable WORKFLOW_ALLOW_BROKEN_PUBLISH=1.
|
|
50
|
+
- name: Pre-publish smoke test
|
|
51
|
+
if: ${{ vars.WORKFLOW_ALLOW_BROKEN_PUBLISH != '1' }}
|
|
52
|
+
run: bash .github/workflows/scripts/smoke-test-pkg.sh hq
|
|
53
|
+
|
|
54
|
+
- name: WARN — pre-publish smoke bypassed
|
|
55
|
+
if: ${{ vars.WORKFLOW_ALLOW_BROKEN_PUBLISH == '1' }}
|
|
56
|
+
run: |
|
|
57
|
+
echo "::warning::WORKFLOW_ALLOW_BROKEN_PUBLISH=1 — pre-publish cross-package smoke test was BYPASSED. This release may ship a DOA package."
|
|
58
|
+
|
|
59
|
+
# Trusted-publisher OIDC; no NODE_AUTH_TOKEN. No --provenance (private
|
|
60
|
+
# repo limitation since npm 2026-05-10). Pre-release versions (with '-')
|
|
61
|
+
# get --tag rc to keep latest dist-tag pointing at stable releases only.
|
|
62
|
+
- name: Publish to npm
|
|
63
|
+
id: publish
|
|
64
|
+
run: |
|
|
65
|
+
NAME=$(jq -r .name package.json)
|
|
66
|
+
VER=$(jq -r .version package.json)
|
|
67
|
+
if npm view "$NAME@$VER" version >/dev/null 2>&1; then
|
|
68
|
+
echo "$NAME@$VER already on npm — skipping"
|
|
69
|
+
else
|
|
70
|
+
if echo "$VER" | grep -q '-'; then
|
|
71
|
+
npm publish --access public --tag rc
|
|
72
|
+
else
|
|
73
|
+
npm publish --access public
|
|
74
|
+
fi
|
|
75
|
+
echo "published=true" >> "$GITHUB_OUTPUT"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
- name: Upload sourcemaps to Sentry
|
|
79
|
+
if: steps.publish.outputs.published == 'true'
|
|
80
|
+
run: |
|
|
81
|
+
VER=$(jq -r .version package.json)
|
|
82
|
+
npx -y @sentry/cli@^2 sourcemaps upload --release "hq-cli@$VER" dist/
|
|
83
|
+
env:
|
|
84
|
+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
85
|
+
SENTRY_ORG: indigo-d0
|
|
86
|
+
SENTRY_PROJECT: hq
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# smoke-test-pkg.sh — pre-publish cross-package import smoke test (Bug A guard).
|
|
3
|
+
#
|
|
4
|
+
# Single-package adaptation of the same script used in the legacy hq monorepo.
|
|
5
|
+
# The monorepo version operated on `packages/$PKG/package.json`; this version
|
|
6
|
+
# reads `./package.json` because each extracted repo is a single package at
|
|
7
|
+
# its root.
|
|
8
|
+
#
|
|
9
|
+
# Install the package in an isolated tmpdir — using a freshly-packed tarball
|
|
10
|
+
# when the package.json version is new, or fetching the existing name@version
|
|
11
|
+
# from npm when it is already published — then boot the binary with
|
|
12
|
+
# `--version`. The boot exercises the full module-load graph and the
|
|
13
|
+
# transitive `@indigoai-us/*` resolution path a customer's `npm install`
|
|
14
|
+
# would take. If the boot fails the script exits non-zero and the workflow
|
|
15
|
+
# aborts before any further publish steps run.
|
|
16
|
+
#
|
|
17
|
+
# Why we don't skip when the consumer's version is already on npm:
|
|
18
|
+
# @indigoai-us/hq-cli depends on @indigoai-us/hq-cloud via a caret range
|
|
19
|
+
# (^5.1.0). A producer bump (published from indigoai-us/hq-cloud) re-resolves
|
|
20
|
+
# this consumer's transitive deps on the next fresh install, so an already-
|
|
21
|
+
# published consumer is NOT actually "locked." If the new producer drops or
|
|
22
|
+
# renames a symbol the published consumer imports, `hq --version` crashes
|
|
23
|
+
# on every fresh install starting the moment the producer publishes.
|
|
24
|
+
#
|
|
25
|
+
# Usage:
|
|
26
|
+
# smoke-test-pkg.sh <bin-name>
|
|
27
|
+
# Example:
|
|
28
|
+
# smoke-test-pkg.sh hq
|
|
29
|
+
|
|
30
|
+
set -uo pipefail
|
|
31
|
+
|
|
32
|
+
BIN=${1:?usage: smoke-test-pkg.sh <bin-name>}
|
|
33
|
+
|
|
34
|
+
PKG_JSON="package.json"
|
|
35
|
+
if [ ! -f "$PKG_JSON" ]; then
|
|
36
|
+
echo "::error::smoke-test-pkg.sh: $PKG_JSON not found (cwd: $(pwd))"
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
NAME=$(jq -r .name "$PKG_JSON")
|
|
41
|
+
VER=$(jq -r .version "$PKG_JSON")
|
|
42
|
+
|
|
43
|
+
# Pick the install target. Already on npm → install by name@version so the
|
|
44
|
+
# smoke matches what a customer would actually receive. Not yet on npm →
|
|
45
|
+
# pack the local repo; the next publish step will push that exact byte-for-
|
|
46
|
+
# byte content. Either way, `npm install` happens OUTSIDE this repo so
|
|
47
|
+
# transitive @indigoai-us/* deps resolve from the registry.
|
|
48
|
+
INSTALL_SOURCE=""
|
|
49
|
+
if npm view "$NAME@$VER" version >/dev/null 2>&1; then
|
|
50
|
+
INSTALL_TARGET="$NAME@$VER"
|
|
51
|
+
INSTALL_SOURCE="npm registry (consumer version already published)"
|
|
52
|
+
else
|
|
53
|
+
TARBALL_RELATIVE=$(npm pack --silent --pack-destination /tmp)
|
|
54
|
+
if [ -z "$TARBALL_RELATIVE" ]; then
|
|
55
|
+
echo "::error::npm pack produced no tarball for $NAME"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
INSTALL_TARGET="/tmp/$TARBALL_RELATIVE"
|
|
59
|
+
if [ ! -f "$INSTALL_TARGET" ]; then
|
|
60
|
+
echo "::error::expected tarball at $INSTALL_TARGET not found"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
INSTALL_SOURCE="packed tarball ($TARBALL_RELATIVE)"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
echo "::group::smoke test: $NAME@$VER via $INSTALL_SOURCE"
|
|
67
|
+
|
|
68
|
+
SMOKE=$(mktemp -d)
|
|
69
|
+
pushd "$SMOKE" >/dev/null
|
|
70
|
+
|
|
71
|
+
printf '%s' '{"name":"smoke","version":"0.0.0","private":true}' > package.json
|
|
72
|
+
|
|
73
|
+
if ! npm install --no-audit --no-fund --no-package-lock "$INSTALL_TARGET" >/tmp/smoke-install.log 2>&1; then
|
|
74
|
+
echo "::error::pre-publish smoke install FAILED for $NAME@$VER:"
|
|
75
|
+
tail -50 /tmp/smoke-install.log
|
|
76
|
+
popd >/dev/null
|
|
77
|
+
echo "::endgroup::"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
set +e
|
|
82
|
+
OUT=$("./node_modules/.bin/$BIN" --version 2>&1)
|
|
83
|
+
CODE=$?
|
|
84
|
+
set -e
|
|
85
|
+
|
|
86
|
+
popd >/dev/null
|
|
87
|
+
|
|
88
|
+
if [ "$CODE" -ne 0 ]; then
|
|
89
|
+
echo "::error::pre-publish smoke FAILED for $NAME@$VER (exit $CODE):"
|
|
90
|
+
echo "::error::$OUT"
|
|
91
|
+
echo "::error::Likely cause: a cross-package symbol was changed (added, removed, renamed) in another @indigoai-us/* package without keeping this consumer's import in sync. Bump this consumer (or revert the producer change) and re-tag, OR set the repo/workflow variable WORKFLOW_ALLOW_BROKEN_PUBLISH=1 to override (audited)."
|
|
92
|
+
echo "::endgroup::"
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
echo "✓ smoke ok: $NAME@$VER → $BIN --version → $OUT (via $INSTALL_SOURCE)"
|
|
97
|
+
echo "::endgroup::"
|
|
@@ -111,7 +111,32 @@ export interface VaultClient {
|
|
|
111
111
|
* entity.
|
|
112
112
|
*/
|
|
113
113
|
listMyPersonEntities(): Promise<VaultEntity[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Legacy global-uniqueness lookup. Under the per-user-namespace model
|
|
116
|
+
* (hq-pro 2026-05-15) this can return any tenant's entity when more
|
|
117
|
+
* than one user holds the same slug, OR `null` when the caller doesn't
|
|
118
|
+
* have it but a different user does. Kept on the interface for any
|
|
119
|
+
* remaining callers, but `provisionCompany` now uses
|
|
120
|
+
* `checkSlugInMyNamespace` instead — same-slug-different-owner is
|
|
121
|
+
* legitimate and should NOT trigger reuse of the stranger's entity.
|
|
122
|
+
*/
|
|
114
123
|
findCompanyBySlug(slug: string): Promise<VaultEntity | null>;
|
|
124
|
+
/**
|
|
125
|
+
* Caller-scoped slug availability check via
|
|
126
|
+
* `GET /entity/check-slug/me?type=company&slug=...`. Returns
|
|
127
|
+
* `{available: true}` when the caller's namespace
|
|
128
|
+
* (owned ∪ active-member-of, soft-deleted excluded) doesn't hold the
|
|
129
|
+
* slug, or `{available: false, conflictingCompanyUid}` when it does
|
|
130
|
+
* — `provisionCompany` reuses the `conflictingCompanyUid` as the
|
|
131
|
+
* idempotent entity instead of creating a duplicate.
|
|
132
|
+
*/
|
|
133
|
+
checkSlugInMyNamespace(slug: string): Promise<{
|
|
134
|
+
available: boolean;
|
|
135
|
+
conflictingCompanyUid?: string;
|
|
136
|
+
}>;
|
|
137
|
+
/** Fetch a company entity by uid. Used to materialize the entity
|
|
138
|
+
* after `checkSlugInMyNamespace` reports a same-namespace collision. */
|
|
139
|
+
getCompanyByUid(uid: string): Promise<VaultEntity>;
|
|
115
140
|
createCompanyEntity(input: {
|
|
116
141
|
slug: string;
|
|
117
142
|
name: string;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* `initial_sync.ok=false`). Manifest + config may have been written.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
31
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="925baddc-7b12-5ad4-b64f-d7e20bd0daab")}catch(e){}}();
|
|
32
32
|
import chalk from "chalk";
|
|
33
33
|
import * as fs from "node:fs";
|
|
34
34
|
import * as path from "node:path";
|
|
@@ -164,7 +164,20 @@ export function ensureManifestEntryForProvision(hqRoot, slug) {
|
|
|
164
164
|
if (path.basename(expected) !== slug)
|
|
165
165
|
return;
|
|
166
166
|
const dir = companyDirPath(hqRoot, slug);
|
|
167
|
-
|
|
167
|
+
// Must be an actual directory, not a stray file. fs.existsSync returns true
|
|
168
|
+
// for regular files too — without this guard, auto-insert would fire on a
|
|
169
|
+
// file at `companies/<slug>`, provisionCompany would then create the vault
|
|
170
|
+
// entity and patch manifest.yaml before writeCompanyConfig's `mkdir -p .hq`
|
|
171
|
+
// exploded with ENOTDIR. statSync swallows the not-found case so a missing
|
|
172
|
+
// path is treated the same as before (no auto-insert).
|
|
173
|
+
let dirStat;
|
|
174
|
+
try {
|
|
175
|
+
dirStat = fs.statSync(dir);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (!dirStat.isDirectory())
|
|
168
181
|
return;
|
|
169
182
|
const raw = fs.readFileSync(mPath, "utf-8");
|
|
170
183
|
let parsed;
|
|
@@ -282,6 +295,28 @@ export function createDefaultVaultClient(apiUrl, accessToken) {
|
|
|
282
295
|
}
|
|
283
296
|
return data.entity;
|
|
284
297
|
},
|
|
298
|
+
async checkSlugInMyNamespace(slug) {
|
|
299
|
+
const url = `${apiUrl.replace(/\/$/, "")}/entity/check-slug/me?type=company&slug=${encodeURIComponent(slug)}`;
|
|
300
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
301
|
+
if (!res.ok) {
|
|
302
|
+
const body = await safeBody(res);
|
|
303
|
+
throw new ProvisionError(1, `Vault GET /entity/check-slug/me failed: ${res.status} ${res.statusText} — ${body}`);
|
|
304
|
+
}
|
|
305
|
+
return (await res.json());
|
|
306
|
+
},
|
|
307
|
+
async getCompanyByUid(uid) {
|
|
308
|
+
const url = `${apiUrl.replace(/\/$/, "")}/entity/${encodeURIComponent(uid)}`;
|
|
309
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
const body = await safeBody(res);
|
|
312
|
+
throw new ProvisionError(1, `Vault GET /entity/${uid} failed: ${res.status} ${res.statusText} — ${body}`);
|
|
313
|
+
}
|
|
314
|
+
const data = (await res.json());
|
|
315
|
+
if (!data.entity) {
|
|
316
|
+
throw new ProvisionError(1, `Vault GET /entity/${uid} returned 200 with no entity body`);
|
|
317
|
+
}
|
|
318
|
+
return data.entity;
|
|
319
|
+
},
|
|
285
320
|
async createCompanyEntity(input) {
|
|
286
321
|
const url = `${apiUrl.replace(/\/$/, "")}/entity`;
|
|
287
322
|
const body = {
|
|
@@ -298,9 +333,15 @@ export function createDefaultVaultClient(apiUrl, accessToken) {
|
|
|
298
333
|
});
|
|
299
334
|
if (!res.ok) {
|
|
300
335
|
const text = await safeBody(res);
|
|
301
|
-
// 409
|
|
302
|
-
//
|
|
303
|
-
//
|
|
336
|
+
// 409 SLUG_IN_USE_FOR_PERSON: the caller already has the slug
|
|
337
|
+
// in their namespace (owned ∪ active-member-of). Under the
|
|
338
|
+
// per-user-namespace model this is the new same-user-collision
|
|
339
|
+
// signal — distinct from the legacy global EntityAlreadyExists.
|
|
340
|
+
// The CLI normally reaches `createCompanyEntity` only after
|
|
341
|
+
// `checkSlugInMyNamespace` reported `available: true`, so a
|
|
342
|
+
// 409 here means a race between the pre-check and the POST.
|
|
343
|
+
// Surface the response body verbatim so the caller can see the
|
|
344
|
+
// `code` + `conflictingCompanyUid` and resolve / retry.
|
|
304
345
|
throw new ProvisionError(1, `Vault POST /entity failed: ${res.status} ${res.statusText} — ${text}`);
|
|
305
346
|
}
|
|
306
347
|
const data = (await res.json());
|
|
@@ -380,13 +421,51 @@ export async function provisionCompany(options) {
|
|
|
380
421
|
throw new ProvisionError(2, 'No person entity found for this Cognito identity. Run `hq onboard` first to create your HQ identity, then re-run `hq cloud provision company`. (Provision was halted before any cloud-side resources were created.)');
|
|
381
422
|
}
|
|
382
423
|
log(`pre-flight ok — caller has ${persons.length} person entity(ies)`);
|
|
383
|
-
|
|
424
|
+
// Per-user-namespace-aware reuse-or-create. Replaces the legacy
|
|
425
|
+
// global `findCompanyBySlug` lookup, which under the per-user model
|
|
426
|
+
// (hq-pro 2026-05-15) returns ANY tenant's entity when more than one
|
|
427
|
+
// user holds the same slug, OR null when a different user has it —
|
|
428
|
+
// both wrong for the CLI's "reuse mine, or create" intent.
|
|
429
|
+
//
|
|
430
|
+
// `--owner` override: `options.ownerUid`, when set, lets a caller
|
|
431
|
+
// create the entity under a DIFFERENT person's ownership (e.g. an
|
|
432
|
+
// admin provisioning on behalf of someone). `/entity/check-slug/me`
|
|
433
|
+
// answers about the CALLER's namespace, not the target owner's, so
|
|
434
|
+
// the pre-check is meaningless in that case. Codex P2 on PR 7
|
|
435
|
+
// flagged this. The gate: only run the namespace check when the
|
|
436
|
+
// owner is the caller (or defaulted to the caller — i.e. no
|
|
437
|
+
// --owner supplied). On override, fall through to
|
|
438
|
+
// `createCompanyEntity` and let the server's authoritative 409
|
|
439
|
+
// (which IS scoped to the target's namespace, per the
|
|
440
|
+
// callerIsOwner gate on POST /entity in hq-pro PR 67) surface any
|
|
441
|
+
// real conflict.
|
|
442
|
+
//
|
|
443
|
+
// `callerIsOwner` is `true` whenever `options.ownerUid` is unset
|
|
444
|
+
// (defaults to caller server-side) OR — when set — happens to
|
|
445
|
+
// match the caller's own person UID(s) from `listMyPersonEntities`.
|
|
446
|
+
const callerOwnedUids = new Set(persons.map((p) => p.uid));
|
|
447
|
+
const callerIsOwner = !options.ownerUid || callerOwnedUids.has(options.ownerUid);
|
|
448
|
+
let entity;
|
|
384
449
|
let createdEntity = false;
|
|
385
|
-
if (
|
|
386
|
-
|
|
450
|
+
if (callerIsOwner) {
|
|
451
|
+
const slugCheck = await vaultClient.checkSlugInMyNamespace(options.slug);
|
|
452
|
+
if (!slugCheck.available && slugCheck.conflictingCompanyUid) {
|
|
453
|
+
log(`reusing existing vault entity uid=${slugCheck.conflictingCompanyUid} (slug already in caller's namespace)`);
|
|
454
|
+
entity = await vaultClient.getCompanyByUid(slugCheck.conflictingCompanyUid);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
log(`slug available in caller's namespace — creating vault entity`);
|
|
458
|
+
entity = await vaultClient.createCompanyEntity({
|
|
459
|
+
slug: options.slug,
|
|
460
|
+
name: options.name ?? options.slug,
|
|
461
|
+
ownerUid: options.ownerUid,
|
|
462
|
+
});
|
|
463
|
+
createdEntity = true;
|
|
464
|
+
log(`created vault entity uid=${entity.uid}`);
|
|
465
|
+
}
|
|
387
466
|
}
|
|
388
467
|
else {
|
|
389
|
-
log(
|
|
468
|
+
log(`--owner ${options.ownerUid} differs from caller's person(s); skipping namespace pre-check (server authoritatively gates per-target-namespace)`);
|
|
390
469
|
entity = await vaultClient.createCompanyEntity({
|
|
391
470
|
slug: options.slug,
|
|
392
471
|
name: options.name ?? options.slug,
|
|
@@ -536,4 +615,4 @@ export function registerCloudProvisionCommands(program) {
|
|
|
536
615
|
});
|
|
537
616
|
}
|
|
538
617
|
//# sourceMappingURL=cloud-provision.js.map
|
|
539
|
-
//# debugId=
|
|
618
|
+
//# debugId=925baddc-7b12-5ad4-b64f-d7e20bd0daab
|