tines 0.0.136 → 0.0.138
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/dist/index.js +134 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3828,6 +3828,7 @@ function createApiClient(options) {
|
|
|
3828
3828
|
// Projects
|
|
3829
3829
|
listProjects: (params = {}) => get(`/api/v1/projects${query(params)}`),
|
|
3830
3830
|
createProject: (body) => request("POST", "/api/v1/projects", body),
|
|
3831
|
+
listStarters: () => get("/api/v1/projects/starters"),
|
|
3831
3832
|
getProject: (id) => get(`/api/v1/projects/${id}`),
|
|
3832
3833
|
updateProject: (id, body) => request("PATCH", `/api/v1/projects/${id}`, body),
|
|
3833
3834
|
deleteProject: (id, body) => request("DELETE", `/api/v1/projects/${id}`, body),
|
|
@@ -4465,7 +4466,9 @@ async function resolveProject(api, ref) {
|
|
|
4465
4466
|
die(`no project named "${ref}" (have: ${have || "none"})`);
|
|
4466
4467
|
}
|
|
4467
4468
|
async function resolveWorkflow(api, ref) {
|
|
4468
|
-
|
|
4469
|
+
return pickWorkflow((await api.listWorkflows()).items, ref);
|
|
4470
|
+
}
|
|
4471
|
+
function pickWorkflow(items, ref) {
|
|
4469
4472
|
const found = items.find((w) => w.id === ref) ?? (items.filter((w) => w.name === ref).length === 1 ? items.find((w) => w.name === ref) : void 0);
|
|
4470
4473
|
if (found) return found;
|
|
4471
4474
|
if (items.filter((w) => w.name === ref).length > 1) {
|
|
@@ -7970,6 +7973,20 @@ existing sets wholesale when present.
|
|
|
7970
7973
|
Each NEW state should carry a "prompt" \u2014 its initial stage instructions,
|
|
7971
7974
|
created as a state-scoped context item \u2014 or pass --no-prompts to skip.
|
|
7972
7975
|
|
|
7976
|
+
A state may inherit context from another state \u2014 its base \u2014 with
|
|
7977
|
+
"inherits_from". Give the base as "<workflow>/<state>" (the qualified form
|
|
7978
|
+
\`tines workflows show\` prints, so it can be pasted straight back in), as a
|
|
7979
|
+
bare state name to mean one of this same request's states, or as a state id:
|
|
7980
|
+
|
|
7981
|
+
{ "id": "wfs_abc", "name": "Merging", "category": "active",
|
|
7982
|
+
"inherits_from": "Engineering/Review" }
|
|
7983
|
+
|
|
7984
|
+
Items scoped to the base are part of an issue's context in the child state,
|
|
7985
|
+
stitched before the child's own layer. Chains are at most 3 states long and
|
|
7986
|
+
may not cycle. On an EXISTING state (one with an "id") the field is
|
|
7987
|
+
merge-patch: leaving it out keeps the current base, and "inherits_from": null
|
|
7988
|
+
clears it. See \`tines workflows bases\` for the pointers already in place.
|
|
7989
|
+
|
|
7973
7990
|
A transition may declare artifact requirements ("requires"): it can then only
|
|
7974
7991
|
be taken once a FRESH artifact with that name \u2014 attached (or reaffirmed)
|
|
7975
7992
|
since the issue entered its current state \u2014 exists on the issue:
|
|
@@ -7983,18 +8000,92 @@ since the issue entered its current state \u2014 exists on the issue:
|
|
|
7983
8000
|
"image/"; file/text only) are optional narrowing; "artifact" is the slot
|
|
7984
8001
|
name issues must carry (see: tines issues artifacts --help).
|
|
7985
8002
|
`;
|
|
7986
|
-
function
|
|
8003
|
+
async function loadLibrary(api) {
|
|
8004
|
+
const workflows = await listAll((page) => api.listWorkflows(page));
|
|
8005
|
+
const states = /* @__PURE__ */ new Map();
|
|
8006
|
+
for (const workflow of workflows) {
|
|
8007
|
+
for (const state of workflow.states) states.set(state.id, { workflow, state });
|
|
8008
|
+
}
|
|
8009
|
+
const children = /* @__PURE__ */ new Map();
|
|
8010
|
+
for (const entry of states.values()) {
|
|
8011
|
+
const base = entry.state.inherits_from;
|
|
8012
|
+
if (base === null) continue;
|
|
8013
|
+
const siblings = children.get(base);
|
|
8014
|
+
if (siblings) siblings.push(entry);
|
|
8015
|
+
else children.set(base, [entry]);
|
|
8016
|
+
}
|
|
8017
|
+
return { workflows, states, children };
|
|
8018
|
+
}
|
|
8019
|
+
var qualify = (entry) => `${entry.workflow.name} / ${entry.state.name}`;
|
|
8020
|
+
function stateLabel(lib, id) {
|
|
8021
|
+
const entry = lib.states.get(id);
|
|
8022
|
+
return entry ? qualify(entry) : id;
|
|
8023
|
+
}
|
|
8024
|
+
function inheritanceLines(lib, state) {
|
|
8025
|
+
const lines = [];
|
|
8026
|
+
if (state.inherits_from !== null) {
|
|
8027
|
+
lines.push(` inherits from: ${stateLabel(lib, state.inherits_from)}`);
|
|
8028
|
+
}
|
|
8029
|
+
const kids = lib.children.get(state.id) ?? [];
|
|
8030
|
+
if (kids.length > 0) lines.push(` inherited by: ${kids.map(qualify).join(", ")}`);
|
|
8031
|
+
return lines;
|
|
8032
|
+
}
|
|
8033
|
+
async function resolveStateBases(states, library) {
|
|
8034
|
+
if (!Array.isArray(states)) return;
|
|
8035
|
+
let lib;
|
|
8036
|
+
for (const entry of states) {
|
|
8037
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
8038
|
+
const state = entry;
|
|
8039
|
+
if (typeof state.inherits_from !== "string" || !state.inherits_from.includes("/")) continue;
|
|
8040
|
+
lib ??= await library();
|
|
8041
|
+
state.inherits_from = resolveBasePair(
|
|
8042
|
+
lib,
|
|
8043
|
+
state.inherits_from,
|
|
8044
|
+
typeof state.name === "string" ? state.name : "(unnamed)"
|
|
8045
|
+
);
|
|
8046
|
+
}
|
|
8047
|
+
}
|
|
8048
|
+
function resolveBasePair(lib, pair, stateName) {
|
|
8049
|
+
const sep2 = pair.indexOf("/");
|
|
8050
|
+
const workflowRef = pair.slice(0, sep2).trim();
|
|
8051
|
+
const stateRef = pair.slice(sep2 + 1).trim();
|
|
8052
|
+
const where = `state "${stateName}" inherits from "${pair}"`;
|
|
8053
|
+
if (!workflowRef || !stateRef) {
|
|
8054
|
+
die(`${where}, which is not a <workflow>/<state> pair`);
|
|
8055
|
+
}
|
|
8056
|
+
const byName = lib.workflows.filter((w) => w.name === workflowRef);
|
|
8057
|
+
if (byName.length > 1)
|
|
8058
|
+
die(`${where}, but workflow name "${workflowRef}" is ambiguous; use an id`);
|
|
8059
|
+
const workflow = lib.workflows.find((w) => w.id === workflowRef) ?? byName[0];
|
|
8060
|
+
if (!workflow) {
|
|
8061
|
+
die(
|
|
8062
|
+
`${where}, but there is no workflow "${workflowRef}" (have: ${lib.workflows.map((w) => w.name).join(", ")})`
|
|
8063
|
+
);
|
|
8064
|
+
}
|
|
8065
|
+
const state = workflow.states.find((s) => s.name === stateRef) ?? workflow.states.find((s) => s.id === stateRef);
|
|
8066
|
+
if (!state) {
|
|
8067
|
+
die(
|
|
8068
|
+
`${where}, but workflow "${workflow.name}" has no state "${stateRef}" (have: ${workflow.states.map((s) => s.name).join(", ")})`
|
|
8069
|
+
);
|
|
8070
|
+
}
|
|
8071
|
+
return state.id;
|
|
8072
|
+
}
|
|
8073
|
+
function printWorkflowDetail(wf, lib) {
|
|
7987
8074
|
console.log(`${wf.name}${wf.is_system ? " (standard, read-only)" : ""} [${wf.id}]`);
|
|
7988
8075
|
if (wf.description) console.log(wf.description);
|
|
7989
8076
|
console.log("\nstates:");
|
|
7990
8077
|
const byId = new Map(wf.states.map((s) => [s.id, s]));
|
|
7991
|
-
|
|
8078
|
+
const rows = wf.states.length === 0 ? [] : formatTable(
|
|
7992
8079
|
wf.states.map((s) => [
|
|
7993
8080
|
` ${s.name}`,
|
|
7994
8081
|
s.category,
|
|
7995
8082
|
s.id === wf.initial_state_id ? "(initial)" : ""
|
|
7996
8083
|
])
|
|
7997
|
-
);
|
|
8084
|
+
).split("\n");
|
|
8085
|
+
for (const [i, row] of rows.entries()) {
|
|
8086
|
+
console.log(row);
|
|
8087
|
+
for (const line of inheritanceLines(lib, wf.states[i])) console.log(line);
|
|
8088
|
+
}
|
|
7998
8089
|
console.log("\ntransitions:");
|
|
7999
8090
|
for (const t of wf.transitions) {
|
|
8000
8091
|
console.log(
|
|
@@ -8034,10 +8125,38 @@ function register11(program3) {
|
|
|
8034
8125
|
withCommon(
|
|
8035
8126
|
workflows.command("show <id-or-name>").description("Show a workflow with states and transitions")
|
|
8036
8127
|
).action(async (ref, opts) => {
|
|
8037
|
-
const
|
|
8038
|
-
const wf =
|
|
8128
|
+
const lib = await loadLibrary(client(opts));
|
|
8129
|
+
const wf = pickWorkflow(lib.workflows, ref);
|
|
8039
8130
|
if (opts.json) return printJson(wf);
|
|
8040
|
-
printWorkflowDetail(wf);
|
|
8131
|
+
printWorkflowDetail(wf, lib);
|
|
8132
|
+
});
|
|
8133
|
+
withCommon(
|
|
8134
|
+
workflows.command("bases").description("List the states other states inherit context from, with their children")
|
|
8135
|
+
).action(async (opts) => {
|
|
8136
|
+
const lib = await loadLibrary(client(opts));
|
|
8137
|
+
const bases = [...lib.states.values()].filter((e) => lib.children.has(e.state.id));
|
|
8138
|
+
if (opts.json) {
|
|
8139
|
+
const ref = (e) => ({
|
|
8140
|
+
workflow: { id: e.workflow.id, name: e.workflow.name },
|
|
8141
|
+
state: { id: e.state.id, name: e.state.name }
|
|
8142
|
+
});
|
|
8143
|
+
return printJson(
|
|
8144
|
+
bases.map((b) => ({
|
|
8145
|
+
...ref(b),
|
|
8146
|
+
inherited_by: lib.children.get(b.state.id).map(ref)
|
|
8147
|
+
}))
|
|
8148
|
+
);
|
|
8149
|
+
}
|
|
8150
|
+
if (bases.length === 0) return console.log("no base states");
|
|
8151
|
+
let group;
|
|
8152
|
+
for (const base of bases) {
|
|
8153
|
+
if (base.workflow.id !== group) {
|
|
8154
|
+
console.log(`${group === void 0 ? "" : "\n"}${base.workflow.name}`);
|
|
8155
|
+
group = base.workflow.id;
|
|
8156
|
+
}
|
|
8157
|
+
console.log(` ${base.state.name}`);
|
|
8158
|
+
console.log(` inherited by: ${lib.children.get(base.state.id).map(qualify).join(", ")}`);
|
|
8159
|
+
}
|
|
8041
8160
|
});
|
|
8042
8161
|
withCommon(
|
|
8043
8162
|
workflows.command("create [json]").description(
|
|
@@ -8053,11 +8172,13 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8053
8172
|
);
|
|
8054
8173
|
}
|
|
8055
8174
|
assertNewStatesHavePrompts(body.states, opts.prompts);
|
|
8056
|
-
const
|
|
8175
|
+
const api = client(opts);
|
|
8176
|
+
await resolveStateBases(body.states, () => loadLibrary(api));
|
|
8177
|
+
const wf = await api.createWorkflow(body);
|
|
8057
8178
|
if (opts.json) return printJson(wf);
|
|
8058
8179
|
console.log(`created workflow "${wf.name}" (${wf.id})
|
|
8059
8180
|
`);
|
|
8060
|
-
printWorkflowDetail(wf);
|
|
8181
|
+
printWorkflowDetail(wf, await loadLibrary(api));
|
|
8061
8182
|
}
|
|
8062
8183
|
);
|
|
8063
8184
|
withCommon(
|
|
@@ -8065,9 +8186,11 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8065
8186
|
).action(
|
|
8066
8187
|
async (ref, inline, opts) => {
|
|
8067
8188
|
const api = client(opts);
|
|
8068
|
-
const
|
|
8189
|
+
const lib = await loadLibrary(api);
|
|
8190
|
+
const wf = pickWorkflow(lib.workflows, ref);
|
|
8069
8191
|
const body = readJsonBody(inline, opts.file) ?? {};
|
|
8070
8192
|
assertNewStatesHavePrompts(body.states, opts.prompts);
|
|
8193
|
+
await resolveStateBases(body.states, async () => lib);
|
|
8071
8194
|
if (opts.name !== void 0) body.name = opts.name;
|
|
8072
8195
|
if (opts.description !== void 0) body.description = opts.description;
|
|
8073
8196
|
if (opts.initialState !== void 0) body.initial_state = opts.initialState;
|
|
@@ -8078,7 +8201,7 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8078
8201
|
if (opts.json) return printJson(updated);
|
|
8079
8202
|
console.log(`updated workflow "${updated.name}" (${updated.id})
|
|
8080
8203
|
`);
|
|
8081
|
-
printWorkflowDetail(updated);
|
|
8204
|
+
printWorkflowDetail(updated, await loadLibrary(api));
|
|
8082
8205
|
}
|
|
8083
8206
|
);
|
|
8084
8207
|
withCommon(
|