refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
* 2. The launcher preamble's shell check — the part that actually protects a
|
||||
* workflow, because it runs on the CALLER side before a mutating verb.
|
||||
*
|
||||
* The preamble tests are behavioral: they stand up a fake `gsd-tools` on PATH
|
||||
* The preamble tests are behavioral: they stand up a fake `msd-tools` on PATH
|
||||
* and assert on what the preamble does, never on the snippet's text.
|
||||
*/
|
||||
|
||||
@@ -31,11 +31,11 @@ const {
|
||||
EXPECTED_PACKAGE_NAME,
|
||||
IDENTITY_STATUS,
|
||||
IDENTITY_RAW_PREFIX,
|
||||
} = require('../gsd-core/bin/lib/runtime-identity.cjs');
|
||||
} = require('../msd-core/bin/lib/runtime-identity.cjs');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const GSD_TOOLS = path.join(REPO_ROOT, 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const SNIPPET = path.join(REPO_ROOT, 'gsd-core', 'workflows', '_runtime-launcher.snippet.sh');
|
||||
const MSD_TOOLS = path.join(REPO_ROOT, 'msd-core', 'bin', 'msd-tools.cjs');
|
||||
const SNIPPET = path.join(REPO_ROOT, 'msd-core', 'workflows', '_runtime-launcher.snippet.sh');
|
||||
|
||||
const okStdout = (name = EXPECTED_PACKAGE_NAME, version = '1.2.3') =>
|
||||
JSON.stringify({ packageName: name, version });
|
||||
@@ -51,7 +51,7 @@ describe('classifyIdentityProbe', () => {
|
||||
test('classifies predecessor usage text as no_identity_verb', () => {
|
||||
// Shape verified against get-shit-done-cc@1.42.3: exit 1 + usage, no JSON.
|
||||
const v = classifyIdentityProbe({
|
||||
stdout: 'Usage: gsd-sdk <command> [args] [options]\n\nCommands:\n run <prompt>',
|
||||
stdout: 'Usage: msd-sdk <command> [args] [options]\n\nCommands:\n run <prompt>',
|
||||
exitCode: 1,
|
||||
});
|
||||
assert.equal(v.reason, 'no_identity_verb');
|
||||
@@ -233,7 +233,7 @@ describe('classifyIdentityProbe', () => {
|
||||
// REGRESSION PIN: these pass today and must keep passing — the predecessor
|
||||
// and any answer that proves nothing still fall through to no_identity_verb.
|
||||
for (const stdout of [
|
||||
'usage: gsd-tools ...',
|
||||
'usage: msd-tools ...',
|
||||
'[]',
|
||||
'0',
|
||||
'null',
|
||||
@@ -285,23 +285,23 @@ describe('buildIdentityPayload', () => {
|
||||
|
||||
describe('explainVerdict', () => {
|
||||
test('names both plausible causes for a missing verb', () => {
|
||||
const v = classifyIdentityProbe({ stdout: 'Usage: gsd-sdk', exitCode: 1 });
|
||||
const msg = explainVerdict(v, '/usr/local/bin/gsd-tools');
|
||||
const v = classifyIdentityProbe({ stdout: 'Usage: msd-sdk', exitCode: 1 });
|
||||
const msg = explainVerdict(v, '/usr/local/bin/msd-tools');
|
||||
assert.match(msg, /different package/);
|
||||
// A legitimate older gsd-core also lacks the verb — the message must say so.
|
||||
// A legitimate older msd-core also lacks the verb — the message must say so.
|
||||
assert.match(msg, /predating the verb/);
|
||||
assert.match(msg, /\/usr\/local\/bin\/gsd-tools/);
|
||||
assert.match(msg, /\/usr\/local\/bin\/msd-tools/);
|
||||
});
|
||||
|
||||
test('names the foreign package on a mismatch', () => {
|
||||
const v = classifyIdentityProbe({ stdout: okStdout('some-other-pkg'), exitCode: 0 });
|
||||
assert.match(explainVerdict(v, '/x/gsd-tools'), /some-other-pkg/);
|
||||
assert.match(explainVerdict(v, '/x/msd-tools'), /some-other-pkg/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('runtime-identity verb', () => {
|
||||
test('emits a parseable payload naming this package', () => {
|
||||
const r = runNode([GSD_TOOLS, 'runtime-identity'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
const r = runNode([MSD_TOOLS, 'runtime-identity'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
assert.equal(r.outcome, OUTCOME.EXITED);
|
||||
assert.equal(r.exitCode, 0);
|
||||
const parsed = JSON.parse(r.stdout);
|
||||
@@ -310,12 +310,12 @@ describe('runtime-identity verb', () => {
|
||||
});
|
||||
|
||||
test('the real verb output classifies as ok', () => {
|
||||
const r = runNode([GSD_TOOLS, 'runtime-identity'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
const r = runNode([MSD_TOOLS, 'runtime-identity'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
assert.equal(classifyIdentityProbe({ stdout: r.stdout, exitCode: r.exitCode }).reason, 'ok');
|
||||
});
|
||||
|
||||
test('--raw emits the same identity on a single line', () => {
|
||||
const r = runNode([GSD_TOOLS, 'runtime-identity', '--raw'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
const r = runNode([MSD_TOOLS, 'runtime-identity', '--raw'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.stdout.trim().split('\n').length, 1);
|
||||
assert.equal(JSON.parse(r.stdout).packageName, EXPECTED_PACKAGE_NAME);
|
||||
@@ -326,8 +326,8 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
let dir;
|
||||
let binDir;
|
||||
|
||||
// The predecessor package `get-shit-done-cc` publishes a `gsd-tools` bin but
|
||||
// NO `gsd_run`. Preferring `gsd_run` is therefore what makes the colliding
|
||||
// The predecessor package `get-shit-done-cc` publishes a `msd-tools` bin but
|
||||
// NO `msd_run`. Preferring `msd_run` is therefore what makes the colliding
|
||||
// name unreachable from PATH — this suite pins that, and pins that we never
|
||||
// fall back to executing the foreign binary (#3146, #3129).
|
||||
const writeFake = (name, body) => {
|
||||
@@ -348,7 +348,7 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
interpreter: '/bin/sh',
|
||||
cwd: dir,
|
||||
timeoutMs: PROBE_TIMEOUT_MS,
|
||||
// PATH holds ONLY our fixture bin, so a real gsd_run installed on the
|
||||
// PATH holds ONLY our fixture bin, so a real msd_run installed on the
|
||||
// developer's machine cannot silently satisfy the resolver and turn a
|
||||
// failing case green.
|
||||
env: { PATH: binDir, HOME: dir, RUNTIME_DIR: dir },
|
||||
@@ -362,7 +362,7 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
dir = createTempDir('gsd-3146-resolver-');
|
||||
dir = createTempDir('msd-3146-resolver-');
|
||||
binDir = path.join(dir, 'bin');
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
// node must be reachable without putting its real directory on PATH.
|
||||
@@ -371,35 +371,35 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
|
||||
afterEach(() => cleanup(dir));
|
||||
|
||||
test('prefers gsd_run when a foreign gsd-tools is also on PATH', (t) => {
|
||||
test('prefers msd_run when a foreign msd-tools is also on PATH', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
const ours = path.join(dir, 'OURS');
|
||||
// #4834: the PATH arm is identity-gated, so the fake must answer
|
||||
// `runtime-identity --raw` the way a real same-package install does.
|
||||
writeFake('gsd_run', `if [ "$1" = "runtime-identity" ] && [ "$2" = "--raw" ]; then echo '{"packageName":"@opengsd/gsd-core","version":"1.0.0-test"}'; else : > "${ours}"; fi`);
|
||||
writeFake('msd_run', `if [ "$1" = "runtime-identity" ] && [ "$2" = "--raw" ]; then echo '{"packageName":"@golem15/msd-core","version":"1.0.0-test"}'; else : > "${ours}"; fi`);
|
||||
const foreign = path.join(dir, 'FOREIGN');
|
||||
writeFake('gsd-tools', `: > "${foreign}"`);
|
||||
writeFake('msd-tools', `: > "${foreign}"`);
|
||||
|
||||
sourceAndRun(`. "${SNIPPET}"; gsd_run query anything`);
|
||||
sourceAndRun(`. "${SNIPPET}"; msd_run query anything`);
|
||||
|
||||
assert.equal(fs.existsSync(ours), true, 'our gsd_run should have run');
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign gsd-tools must never run');
|
||||
assert.equal(fs.existsSync(ours), true, 'our msd_run should have run');
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign msd-tools must never run');
|
||||
});
|
||||
|
||||
test('never executes a foreign gsd-tools when no gsd_run is reachable', (t) => {
|
||||
test('never executes a foreign msd-tools when no msd_run is reachable', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
const foreign = path.join(dir, 'FOREIGN');
|
||||
writeFake('gsd-tools', `: > "${foreign}"`);
|
||||
writeFake('msd-tools', `: > "${foreign}"`);
|
||||
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; gsd_run query anything`);
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; msd_run query anything`);
|
||||
|
||||
// Fail closed: the resolver reaches its final else and errors, rather than
|
||||
// silently handing a state-mutating verb to a package it was not written for.
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign gsd-tools must never run');
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign msd-tools must never run');
|
||||
assert.notEqual(r.exitCode, 0);
|
||||
});
|
||||
|
||||
// Regression: a second source finds the `gsd_run` FUNCTION, so `command -v`
|
||||
// Regression: a second source finds the `msd_run` FUNCTION, so `command -v`
|
||||
// returns a bare name rather than a path. A revision that guarded on `-x`
|
||||
// rejected it, fell through every branch, and hit the resolver's `exit 1` —
|
||||
// which, in a SOURCED script, terminates the caller's shell. `unset -f` at
|
||||
@@ -410,30 +410,30 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
const alive = path.join(dir, 'ALIVE');
|
||||
// #4834: the stub must prove identity — a bare stub now falls through the
|
||||
// gate and would land in the resolver's exit 1 on the second source.
|
||||
writeFake('gsd_run', `if [ "$1" = "runtime-identity" ] && [ "$2" = "--raw" ]; then echo '{"packageName":"@opengsd/gsd-core","version":"1.0.0-test"}'; else : > "${ours}"; fi`);
|
||||
writeFake('msd_run', `if [ "$1" = "runtime-identity" ] && [ "$2" = "--raw" ]; then echo '{"packageName":"@golem15/msd-core","version":"1.0.0-test"}'; else : > "${ours}"; fi`);
|
||||
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; . "${SNIPPET}"; : > "${alive}"; gsd_run query anything`);
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; . "${SNIPPET}"; : > "${alive}"; msd_run query anything`);
|
||||
|
||||
assert.equal(fs.existsSync(alive), true, 'the second source must not kill the shell');
|
||||
assert.equal(fs.existsSync(ours), true, 'the launcher must still work after re-sourcing');
|
||||
assert.equal(r.exitCode, 0);
|
||||
});
|
||||
|
||||
// A non-executable file named `gsd_run` must never be selected. This is what
|
||||
// A non-executable file named `msd_run` must never be selected. This is what
|
||||
// makes an explicit `-x` guard unnecessary: PATH search already requires an
|
||||
// executable, so `command -v` simply does not return this file. An earlier
|
||||
// revision carried such a guard, and it caused a worse bug — on a second
|
||||
// source it rejected the shell FUNCTION's bare name and fell through to the
|
||||
// resolver's `exit 1`, which kills a SOURCED caller's shell.
|
||||
test('never selects a non-executable gsd_run, and still refuses the foreign bin', (t) => {
|
||||
test('never selects a non-executable msd_run, and still refuses the foreign bin', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
fs.writeFileSync(path.join(binDir, 'gsd_run'), 'not executable\n', { mode: 0o644 });
|
||||
fs.writeFileSync(path.join(binDir, 'msd_run'), 'not executable\n', { mode: 0o644 });
|
||||
const foreign = path.join(dir, 'FOREIGN');
|
||||
writeFake('gsd-tools', `: > "${foreign}"`);
|
||||
writeFake('msd-tools', `: > "${foreign}"`);
|
||||
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; gsd_run query anything`);
|
||||
const r = sourceAndRun(`. "${SNIPPET}"; msd_run query anything`);
|
||||
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign gsd-tools must never run');
|
||||
assert.equal(fs.existsSync(foreign), false, 'the foreign msd-tools must never run');
|
||||
assert.notEqual(r.exitCode, 0, 'resolution must fail closed');
|
||||
});
|
||||
});
|
||||
@@ -441,12 +441,12 @@ describe('launcher resolver: PATH branch prefers the collision-free bin', () =>
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// #3841 — the launcher preamble ASSERTS identity before any verb runs.
|
||||
//
|
||||
// #3831 closed the PATH route structurally (the resolver takes `gsd_run`, which
|
||||
// #3831 closed the PATH route structurally (the resolver takes `msd_run`, which
|
||||
// only this package publishes). The path-based branches — a project-local
|
||||
// install, a runtime config directory — had no such guarantee: they trusted
|
||||
// their configured location. These tests cover the assertion that closes them.
|
||||
//
|
||||
// Every shell assertion below reads the TYPED `GSD_IDENTITY_STATUS` value, never
|
||||
// Every shell assertion below reads the TYPED `MSD_IDENTITY_STATUS` value, never
|
||||
// the warning prose: CONTRIBUTING forbids `assert.match` on process output as
|
||||
// the proof a behavior fired.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -456,7 +456,7 @@ describe('statusForVerdict — the five-way reason collapses to two shell values
|
||||
const cases = [
|
||||
[{ stdout: JSON.stringify(buildIdentityPayload({ readVersion: () => '1.0.0' })), exitCode: 0 }, IDENTITY_STATUS.OK],
|
||||
[{ stdout: okStdout('get-shit-done-cc'), exitCode: 0 }, IDENTITY_STATUS.UNVERIFIED],
|
||||
[{ stdout: 'Usage: gsd-sdk', exitCode: 1 }, IDENTITY_STATUS.UNVERIFIED],
|
||||
[{ stdout: 'Usage: msd-sdk', exitCode: 1 }, IDENTITY_STATUS.UNVERIFIED],
|
||||
[{ stdout: 'not json', exitCode: 0 }, IDENTITY_STATUS.UNVERIFIED],
|
||||
[{ stdout: '', exitCode: null, spawnFailed: true }, IDENTITY_STATUS.UNVERIFIED],
|
||||
];
|
||||
@@ -502,7 +502,7 @@ describe('IDENTITY_RAW_PREFIX — the anchor the shell matches on', () => {
|
||||
// order or serialization ever changed, the preamble would silently stop
|
||||
// verifying a legitimate install and warn on every run.
|
||||
test('the real --raw payload starts with the prefix', () => {
|
||||
const r = runNode([GSD_TOOLS, 'runtime-identity', '--raw'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
const r = runNode([MSD_TOOLS, 'runtime-identity', '--raw'], { timeoutMs: PROBE_TIMEOUT_MS });
|
||||
assert.equal(r.exitCode, 0);
|
||||
assert.equal(r.stdout.startsWith(IDENTITY_RAW_PREFIX), true);
|
||||
});
|
||||
@@ -520,9 +520,9 @@ const skipOnWindows = (t) => {
|
||||
|
||||
/**
|
||||
* Per-describe fixture for driving the REAL launcher preamble against a fake
|
||||
* `gsd-tools`. Shared by the two describes below rather than retyped: they need
|
||||
* `msd-tools`. Shared by the two describes below rather than retyped: they need
|
||||
* the same temp tree (a `bin/` holding only a `node` symlink, plus
|
||||
* `gsd-core/bin/gsd-tools.cjs`) and the same restricted-PATH invocation, and a
|
||||
* `msd-core/bin/msd-tools.cjs`) and the same restricted-PATH invocation, and a
|
||||
* second copy is a place for the two to drift apart.
|
||||
*
|
||||
* Returns handles rather than installing its own beforeEach/afterEach, so each
|
||||
@@ -534,9 +534,9 @@ function makeIdentityFixture() {
|
||||
let toolsDir;
|
||||
|
||||
const setup = () => {
|
||||
dir = createTempDir('gsd-3841-identity-');
|
||||
dir = createTempDir('msd-3841-identity-');
|
||||
binDir = path.join(dir, 'bin');
|
||||
toolsDir = path.join(dir, 'gsd-core', 'bin');
|
||||
toolsDir = path.join(dir, 'msd-core', 'bin');
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
fs.mkdirSync(toolsDir, { recursive: true });
|
||||
fs.symlinkSync(process.execPath, path.join(binDir, 'node'));
|
||||
@@ -549,7 +549,7 @@ function makeIdentityFixture() {
|
||||
// assertion exists for.
|
||||
const installFakeTool = (identityBody) => {
|
||||
fs.writeFileSync(
|
||||
path.join(toolsDir, 'gsd-tools.cjs'),
|
||||
path.join(toolsDir, 'msd-tools.cjs'),
|
||||
'#!/usr/bin/env node\n' +
|
||||
"const a = process.argv.slice(2);\n" +
|
||||
"if (a[0] === 'runtime-identity') { " + identityBody + " }\n" +
|
||||
@@ -564,7 +564,7 @@ function makeIdentityFixture() {
|
||||
harness,
|
||||
'#!/bin/sh\n' +
|
||||
`. "${SNIPPET}"\n` +
|
||||
"printf 'STATUS=%s\\n' \"$GSD_IDENTITY_STATUS\"\n" +
|
||||
"printf 'STATUS=%s\\n' \"$MSD_IDENTITY_STATUS\"\n" +
|
||||
extra +
|
||||
'\n',
|
||||
{ mode: 0o755 },
|
||||
@@ -575,7 +575,7 @@ function makeIdentityFixture() {
|
||||
interpreter: '/bin/sh',
|
||||
cwd: dir,
|
||||
timeoutMs: PROBE_TIMEOUT_MS,
|
||||
// PATH holds only the fixture bin (plus a node symlink) so a real gsd_run
|
||||
// PATH holds only the fixture bin (plus a node symlink) so a real msd_run
|
||||
// on the developer's machine cannot satisfy the resolver instead.
|
||||
env: { PATH: binDir, HOME: dir, RUNTIME_DIR: dir },
|
||||
});
|
||||
@@ -631,7 +631,7 @@ describe('launcher preamble: identity assertion on a path-based branch (#3841)',
|
||||
|
||||
test('a payload one byte LONGER than the anchor does not verify (limit+1)', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
// An extra character inside the package name: `@opengsd/gsd-coreX`.
|
||||
// An extra character inside the package name: `@golem15/msd-coreX`.
|
||||
const long = `{"packageName":"${EXPECTED_PACKAGE_NAME}X"`;
|
||||
installFakeTool(emit(`${long},"version":"9.9.9"}`));
|
||||
|
||||
@@ -699,11 +699,11 @@ describe('launcher preamble: identity assertion on a path-based branch (#3841)',
|
||||
test('a tool with no runtime-identity verb does not verify and does not kill the run', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
// Shape of the real predecessor: usage screen, exit 1. Also the shape of an
|
||||
// @opengsd/gsd-core older than the verb — which is why this WARNS rather
|
||||
// @golem15/msd-core older than the verb — which is why this WARNS rather
|
||||
// than failing during the warn-then-fail rollout (#3146 ruling).
|
||||
installFakeTool("process.stderr.write('Unknown command\\n'); process.exit(1);");
|
||||
|
||||
const r = sourceAndReport('gsd_run phases clear');
|
||||
const r = sourceAndReport('msd_run phases clear');
|
||||
|
||||
assert.equal(r.stdout.includes(`STATUS=${IDENTITY_STATUS.UNVERIFIED}`), true, r.stdout);
|
||||
assert.equal(r.exitCode, 0, 'the warn phase must not stop the workflow');
|
||||
@@ -722,7 +722,7 @@ describe('launcher preamble: identity assertion on a path-based branch (#3841)',
|
||||
test('the probe writes nothing to the workflow stdout', (t) => {
|
||||
if (skipOnWindows(t)) return;
|
||||
// The preamble captures the probe in a command substitution. If it leaked,
|
||||
// every workflow that parses gsd_run output would read the payload first.
|
||||
// every workflow that parses msd_run output would read the payload first.
|
||||
installFakeTool(emit(`${IDENTITY_RAW_PREFIX},"version":"1.0.0"}`));
|
||||
|
||||
const r = sourceAndReport();
|
||||
@@ -737,7 +737,7 @@ describe('launcher preamble: identity assertion on a path-based branch (#3841)',
|
||||
// A child process must see it — workflows spawn subshells between steps.
|
||||
// Absolute interpreter: PATH holds only the fixture bin, so a bare `sh`
|
||||
// would not resolve and the child would never run.
|
||||
const r = sourceAndReport('/bin/sh -c \'printf "CHILD=%s\\n" "$GSD_IDENTITY_STATUS"\'');
|
||||
const r = sourceAndReport('/bin/sh -c \'printf "CHILD=%s\\n" "$MSD_IDENTITY_STATUS"\'');
|
||||
|
||||
assert.equal(r.stdout.includes(`CHILD=${IDENTITY_STATUS.OK}`), true, r.stdout);
|
||||
});
|
||||
@@ -760,7 +760,7 @@ describe('shell preamble and classifier agree (cross-surface parity, #3841)', ()
|
||||
['P2 payload matches, exit 1', matching(), 1, true],
|
||||
['P3 foreign payload, exit 0', foreign, 0, false],
|
||||
['P4 foreign payload, exit 1', foreign, 1, false],
|
||||
['P5 predecessor usage text, exit 1', 'usage: gsd-tools <command>', 1, false],
|
||||
['P5 predecessor usage text, exit 1', 'usage: msd-tools <command>', 1, false],
|
||||
[
|
||||
'P6 decoy embeds expected name in another field, exit 0',
|
||||
JSON.stringify({ packageName: 'get-shit-done-cc', note: EXPECTED_PACKAGE_NAME }),
|
||||
|
||||
Reference in New Issue
Block a user